fix(core): improve error type extraction for telemetry (#19565)

Co-authored-by: Yuna Seol <yunaseol@google.com>
This commit is contained in:
Yuna Seol
2026-02-19 16:19:19 -05:00
committed by GitHub
parent ddc5458451
commit 8064973899
4 changed files with 74 additions and 1 deletions

View File

@@ -32,6 +32,7 @@ import { LoggingContentGenerator } from './loggingContentGenerator.js';
import type { Config } from '../config/config.js';
import { UserTierId } from '../code_assist/types.js';
import { ApiRequestEvent, LlmRole } from '../telemetry/types.js';
import { FatalAuthenticationError } from '../utils/errors.js';
describe('LoggingContentGenerator', () => {
let wrapped: ContentGenerator;
@@ -137,6 +138,19 @@ describe('LoggingContentGenerator', () => {
const errorEvent = vi.mocked(logApiError).mock.calls[0][1];
expect(errorEvent.duration_ms).toBe(1000);
});
describe('error type extraction', () => {
it('should extract error type correctly', async () => {
const req = { contents: [], model: 'm' };
const error = new FatalAuthenticationError('test');
vi.mocked(wrapped.generateContent).mockRejectedValue(error);
await expect(
loggingContentGenerator.generateContent(req, 'id', LlmRole.MAIN),
).rejects.toThrow();
const errorEvent = vi.mocked(logApiError).mock.calls[0][1];
expect(errorEvent.error_type).toBe('FatalAuthenticationError');
});
});
});
describe('generateContentStream', () => {

View File

@@ -36,6 +36,7 @@ import { toContents } from '../code_assist/converter.js';
import { isStructuredError } from '../utils/quotaErrorDetection.js';
import { runInDevTraceSpan, type SpanMetadata } from '../telemetry/trace.js';
import { debugLogger } from '../utils/debugLogger.js';
import { getErrorType } from '../utils/errors.js';
interface StructuredError {
status: number;
@@ -167,7 +168,7 @@ export class LoggingContentGenerator implements ContentGenerator {
serverDetails?: ServerDetails,
): void {
const errorMessage = error instanceof Error ? error.message : String(error);
const errorType = error instanceof Error ? error.name : 'unknown';
const errorType = getErrorType(error);
logApiError(
this.config,

View File

@@ -12,6 +12,14 @@ import {
BadRequestError,
ForbiddenError,
getErrorMessage,
getErrorType,
FatalAuthenticationError,
FatalCancellationError,
FatalInputError,
FatalSandboxError,
FatalConfigError,
FatalTurnLimitedError,
FatalToolExecutionError,
} from './errors.js';
describe('getErrorMessage', () => {
@@ -201,3 +209,44 @@ describe('toFriendlyError', () => {
expect(toFriendlyError(error)).toBe(error);
});
});
describe('getErrorType', () => {
it('should return error name for standard errors', () => {
expect(getErrorType(new Error('test'))).toBe('Error');
expect(getErrorType(new TypeError('test'))).toBe('TypeError');
expect(getErrorType(new SyntaxError('test'))).toBe('SyntaxError');
});
it('should return constructor name for custom errors', () => {
expect(getErrorType(new FatalAuthenticationError('test'))).toBe(
'FatalAuthenticationError',
);
expect(getErrorType(new FatalInputError('test'))).toBe('FatalInputError');
expect(getErrorType(new FatalSandboxError('test'))).toBe(
'FatalSandboxError',
);
expect(getErrorType(new FatalConfigError('test'))).toBe('FatalConfigError');
expect(getErrorType(new FatalTurnLimitedError('test'))).toBe(
'FatalTurnLimitedError',
);
expect(getErrorType(new FatalToolExecutionError('test'))).toBe(
'FatalToolExecutionError',
);
expect(getErrorType(new FatalCancellationError('test'))).toBe(
'FatalCancellationError',
);
expect(getErrorType(new ForbiddenError('test'))).toBe('ForbiddenError');
expect(getErrorType(new UnauthorizedError('test'))).toBe(
'UnauthorizedError',
);
expect(getErrorType(new BadRequestError('test'))).toBe('BadRequestError');
});
it('should return "unknown" for non-Error objects', () => {
expect(getErrorType('string error')).toBe('unknown');
expect(getErrorType(123)).toBe('unknown');
expect(getErrorType({})).toBe('unknown');
expect(getErrorType(null)).toBe('unknown');
expect(getErrorType(undefined)).toBe('unknown');
});
});

View File

@@ -26,6 +26,15 @@ export function getErrorMessage(error: unknown): string {
}
}
export function getErrorType(error: unknown): string {
if (!(error instanceof Error)) return 'unknown';
// Return constructor name if the generic 'Error' name is used (for custom errors)
return error.name === 'Error'
? (error.constructor?.name ?? 'Error')
: error.name;
}
export class FatalError extends Error {
constructor(
message: string,