Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/core/src/core/loggingContentGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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', () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/core/loggingContentGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
49 changes: 49 additions & 0 deletions packages/core/src/utils/errors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ import {
BadRequestError,
ForbiddenError,
getErrorMessage,
getErrorType,
FatalAuthenticationError,
FatalCancellationError,
FatalInputError,
FatalSandboxError,
FatalConfigError,
FatalTurnLimitedError,
FatalToolExecutionError,
} from './errors.js';

describe('getErrorMessage', () => {
Expand Down Expand Up @@ -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');
});
});
9 changes: 9 additions & 0 deletions packages/core/src/utils/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading