diff --git a/packages/cli/src/utils/errors.ts b/packages/cli/src/utils/errors.ts index 9d4789b7e4..913fc0d562 100644 --- a/packages/cli/src/utils/errors.ts +++ b/packages/cli/src/utils/errors.ts @@ -19,6 +19,7 @@ import { debugLogger, coreEvents, getErrorMessage, + getErrorType, } from '@google/gemini-cli-core'; import { runSyncCleanup } from './cleanup.js'; @@ -82,7 +83,7 @@ export function handleError( timestamp: new Date().toISOString(), status: 'error', error: { - type: error instanceof Error ? error.constructor.name : 'Error', + type: getErrorType(error), message: errorMessage, }, stats: streamFormatter.convertToStreamStats(metrics, 0), @@ -177,7 +178,7 @@ export function handleCancellationError(config: Config): never { timestamp: new Date().toISOString(), status: 'error', error: { - type: 'FatalCancellationError', + type: getErrorType(cancellationError), message: cancellationError.message, }, stats: streamFormatter.convertToStreamStats(metrics, 0), @@ -218,7 +219,7 @@ export function handleMaxTurnsExceededError(config: Config): never { timestamp: new Date().toISOString(), status: 'error', error: { - type: 'FatalTurnLimitedError', + type: getErrorType(maxTurnsError), message: maxTurnsError.message, }, stats: streamFormatter.convertToStreamStats(metrics, 0), diff --git a/packages/core/src/code_assist/setup.ts b/packages/core/src/code_assist/setup.ts index 5e94aee8c7..a68a1ec550 100644 --- a/packages/core/src/code_assist/setup.ts +++ b/packages/core/src/code_assist/setup.ts @@ -32,6 +32,7 @@ export class ProjectIdRequiredError extends Error { super( 'This account requires setting the GOOGLE_CLOUD_PROJECT or GOOGLE_CLOUD_PROJECT_ID env var. See https://goo.gle/gemini-cli-auth-docs#workspace-gca', ); + this.name = 'ProjectIdRequiredError'; } } @@ -42,6 +43,7 @@ export class ProjectIdRequiredError extends Error { export class ValidationCancelledError extends Error { constructor() { super('User cancelled account validation'); + this.name = 'ValidationCancelledError'; } } @@ -51,6 +53,7 @@ export class IneligibleTierError extends Error { constructor(ineligibleTiers: IneligibleTier[]) { const reasons = ineligibleTiers.map((t) => t.reasonMessage).join(', '); super(reasons); + this.name = 'IneligibleTierError'; this.ineligibleTiers = ineligibleTiers; } } diff --git a/packages/core/src/utils/errors.test.ts b/packages/core/src/utils/errors.test.ts index 63aa4628fb..b4e0771896 100644 --- a/packages/core/src/utils/errors.test.ts +++ b/packages/core/src/utils/errors.test.ts @@ -355,12 +355,29 @@ describe('getErrorType', () => { expect(getErrorType(undefined)).toBe('unknown'); }); - it('should strip leading underscores from error names', () => { - class _GaxiosError extends Error {} + it('should use explicitly set error names', () => { + class _GaxiosError extends Error { + constructor(message: string) { + super(message); + this.name = 'GaxiosError'; + } + } expect(getErrorType(new _GaxiosError('test'))).toBe('GaxiosError'); - const errorWithUnderscoreName = new Error('test'); - errorWithUnderscoreName.name = '_CodeBuddyError'; - expect(getErrorType(errorWithUnderscoreName)).toBe('CodeBuddyError'); + class BadRequestError3 extends Error { + constructor(message: string) { + super(message); + this.name = 'BadRequestError'; + } + } + expect(getErrorType(new BadRequestError3('test'))).toBe('BadRequestError'); + + class _AbortError2 extends Error { + constructor(message: string) { + super(message); + this.name = 'AbortError'; + } + } + expect(getErrorType(new _AbortError2('test'))).toBe('AbortError'); }); }); diff --git a/packages/core/src/utils/errors.ts b/packages/core/src/utils/errors.ts index 834d1e4586..210902029b 100644 --- a/packages/core/src/utils/errors.ts +++ b/packages/core/src/utils/errors.ts @@ -57,9 +57,11 @@ 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) + // Use the constructor name if the standard error name is missing or generic. const name = - error.name === 'Error' ? (error.constructor?.name ?? 'Error') : error.name; + error.name && error.name !== 'Error' + ? error.name + : (error.constructor?.name ?? 'Error'); // Strip leading underscore from error names. Bundlers like esbuild sometimes // rename classes to avoid scope collisions. @@ -72,42 +74,50 @@ export class FatalError extends Error { readonly exitCode: number, ) { super(message); + this.name = 'FatalError'; } } export class FatalAuthenticationError extends FatalError { constructor(message: string) { super(message, 41); + this.name = 'FatalAuthenticationError'; } } export class FatalInputError extends FatalError { constructor(message: string) { super(message, 42); + this.name = 'FatalInputError'; } } export class FatalSandboxError extends FatalError { constructor(message: string) { super(message, 44); + this.name = 'FatalSandboxError'; } } export class FatalConfigError extends FatalError { constructor(message: string) { super(message, 52); + this.name = 'FatalConfigError'; } } export class FatalTurnLimitedError extends FatalError { constructor(message: string) { super(message, 53); + this.name = 'FatalTurnLimitedError'; } } export class FatalToolExecutionError extends FatalError { constructor(message: string) { super(message, 54); + this.name = 'FatalToolExecutionError'; } } export class FatalCancellationError extends FatalError { constructor(message: string) { super(message, 130); // Standard exit code for SIGINT + this.name = 'FatalCancellationError'; } } @@ -118,7 +128,12 @@ export class CanceledError extends Error { } } -export class ForbiddenError extends Error {} +export class ForbiddenError extends Error { + constructor(message: string) { + super(message); + this.name = 'ForbiddenError'; + } +} export class AccountSuspendedError extends ForbiddenError { readonly appealUrl?: string; readonly appealLinkText?: string; @@ -130,8 +145,18 @@ export class AccountSuspendedError extends ForbiddenError { this.appealLinkText = metadata?.['appeal_url_link_text']; } } -export class UnauthorizedError extends Error {} -export class BadRequestError extends Error {} +export class UnauthorizedError extends Error { + constructor(message: string) { + super(message); + this.name = 'UnauthorizedError'; + } +} +export class BadRequestError extends Error { + constructor(message: string) { + super(message); + this.name = 'BadRequestError'; + } +} export class ChangeAuthRequestedError extends Error { constructor() { @@ -264,10 +289,7 @@ export function isAuthenticationError(error: unknown): boolean { } // Check for UnauthorizedError class (from MCP SDK or our own) - if ( - error instanceof Error && - error.constructor.name === 'UnauthorizedError' - ) { + if (error instanceof Error && error.name === 'UnauthorizedError') { return true; }