| |
| |
| |
| |
| |
|
|
| import type { Config } from '@google/gemini-cli-core'; |
| import { |
| OutputFormat, |
| JsonFormatter, |
| StreamJsonFormatter, |
| JsonStreamEventType, |
| uiTelemetryService, |
| parseAndFormatApiError, |
| FatalTurnLimitedError, |
| FatalCancellationError, |
| FatalToolExecutionError, |
| isFatalToolError, |
| debugLogger, |
| coreEvents, |
| getErrorType, |
| getErrorMessage, |
| } from '@google/gemini-cli-core'; |
| import { runSyncCleanup } from './cleanup.js'; |
|
|
| interface ErrorWithCode extends Error { |
| exitCode?: number; |
| code?: string | number; |
| status?: string | number; |
| } |
|
|
| |
| |
| |
| function extractErrorCode(error: unknown): string | number { |
| |
| const errorWithCode = error as ErrorWithCode; |
|
|
| |
| if (typeof errorWithCode.exitCode === 'number') { |
| return errorWithCode.exitCode; |
| } |
| if (errorWithCode.code !== undefined) { |
| return errorWithCode.code; |
| } |
| if (errorWithCode.status !== undefined) { |
| return errorWithCode.status; |
| } |
|
|
| return 1; |
| } |
|
|
| |
| |
| |
| function getNumericExitCode(errorCode: string | number): number { |
| return typeof errorCode === 'number' ? errorCode : 1; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export function handleError( |
| error: unknown, |
| config: Config, |
| customErrorCode?: string | number, |
| ): never { |
| const errorMessage = parseAndFormatApiError( |
| error, |
| config.getContentGeneratorConfig()?.authType, |
| ); |
|
|
| if (config.getOutputFormat() === OutputFormat.STREAM_JSON) { |
| const streamFormatter = new StreamJsonFormatter(); |
| const errorCode = customErrorCode ?? extractErrorCode(error); |
| const metrics = uiTelemetryService.getMetrics(); |
|
|
| streamFormatter.emitEvent({ |
| type: JsonStreamEventType.RESULT, |
| timestamp: new Date().toISOString(), |
| status: 'error', |
| error: { |
| type: getErrorType(error), |
| message: errorMessage, |
| }, |
| stats: streamFormatter.convertToStreamStats(metrics, 0), |
| }); |
|
|
| runSyncCleanup(); |
| process.exit(getNumericExitCode(errorCode)); |
| } else if (config.getOutputFormat() === OutputFormat.JSON) { |
| const formatter = new JsonFormatter(); |
| const errorCode = customErrorCode ?? extractErrorCode(error); |
|
|
| const formattedError = formatter.formatError( |
| error instanceof Error ? error : new Error(getErrorMessage(error)), |
| errorCode, |
| config.getSessionId(), |
| ); |
|
|
| coreEvents.emitFeedback('error', formattedError); |
| runSyncCleanup(); |
| process.exit(getNumericExitCode(errorCode)); |
| } else { |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function handleToolError( |
| toolName: string, |
| toolError: Error, |
| config: Config, |
| errorType?: string, |
| resultDisplay?: string, |
| ): void { |
| const errorMessage = `Error executing tool ${toolName}: ${resultDisplay || toolError.message}`; |
|
|
| const isFatal = isFatalToolError(errorType); |
|
|
| if (isFatal) { |
| const toolExecutionError = new FatalToolExecutionError(errorMessage); |
| if (config.getOutputFormat() === OutputFormat.STREAM_JSON) { |
| const streamFormatter = new StreamJsonFormatter(); |
| const metrics = uiTelemetryService.getMetrics(); |
| streamFormatter.emitEvent({ |
| type: JsonStreamEventType.RESULT, |
| timestamp: new Date().toISOString(), |
| status: 'error', |
| error: { |
| type: errorType ?? 'FatalToolExecutionError', |
| message: toolExecutionError.message, |
| }, |
| stats: streamFormatter.convertToStreamStats(metrics, 0), |
| }); |
| } else if (config.getOutputFormat() === OutputFormat.JSON) { |
| const formatter = new JsonFormatter(); |
| const formattedError = formatter.formatError( |
| toolExecutionError, |
| errorType ?? toolExecutionError.exitCode, |
| config.getSessionId(), |
| ); |
| coreEvents.emitFeedback('error', formattedError); |
| } else { |
| coreEvents.emitFeedback('error', errorMessage); |
| } |
| runSyncCleanup(); |
| process.exit(toolExecutionError.exitCode); |
| } |
|
|
| |
| debugLogger.warn(errorMessage); |
| } |
|
|
| |
| |
| |
| export function handleCancellationError(config: Config): never { |
| const cancellationError = new FatalCancellationError('Operation cancelled.'); |
|
|
| if (config.getOutputFormat() === OutputFormat.STREAM_JSON) { |
| const streamFormatter = new StreamJsonFormatter(); |
| const metrics = uiTelemetryService.getMetrics(); |
| streamFormatter.emitEvent({ |
| type: JsonStreamEventType.RESULT, |
| timestamp: new Date().toISOString(), |
| status: 'error', |
| error: { |
| type: getErrorType(cancellationError), |
| message: cancellationError.message, |
| }, |
| stats: streamFormatter.convertToStreamStats(metrics, 0), |
| }); |
| runSyncCleanup(); |
| process.exit(cancellationError.exitCode); |
| } else if (config.getOutputFormat() === OutputFormat.JSON) { |
| const formatter = new JsonFormatter(); |
| const formattedError = formatter.formatError( |
| cancellationError, |
| cancellationError.exitCode, |
| config.getSessionId(), |
| ); |
|
|
| coreEvents.emitFeedback('error', formattedError); |
| runSyncCleanup(); |
| process.exit(cancellationError.exitCode); |
| } else { |
| coreEvents.emitFeedback('error', cancellationError.message); |
| runSyncCleanup(); |
| process.exit(cancellationError.exitCode); |
| } |
| } |
|
|
| |
| |
| |
| export function handleMaxTurnsExceededError(config: Config): never { |
| const maxTurnsError = new FatalTurnLimitedError( |
| 'Reached max session turns for this session. Increase the number of turns by specifying maxSessionTurns in settings.json.', |
| ); |
|
|
| if (config.getOutputFormat() === OutputFormat.STREAM_JSON) { |
| const streamFormatter = new StreamJsonFormatter(); |
| const metrics = uiTelemetryService.getMetrics(); |
| streamFormatter.emitEvent({ |
| type: JsonStreamEventType.RESULT, |
| timestamp: new Date().toISOString(), |
| status: 'error', |
| error: { |
| type: getErrorType(maxTurnsError), |
| message: maxTurnsError.message, |
| }, |
| stats: streamFormatter.convertToStreamStats(metrics, 0), |
| }); |
| runSyncCleanup(); |
| process.exit(maxTurnsError.exitCode); |
| } else if (config.getOutputFormat() === OutputFormat.JSON) { |
| const formatter = new JsonFormatter(); |
| const formattedError = formatter.formatError( |
| maxTurnsError, |
| maxTurnsError.exitCode, |
| config.getSessionId(), |
| ); |
|
|
| coreEvents.emitFeedback('error', formattedError); |
| runSyncCleanup(); |
| process.exit(maxTurnsError.exitCode); |
| } else { |
| coreEvents.emitFeedback('error', maxTurnsError.message); |
| runSyncCleanup(); |
| process.exit(maxTurnsError.exitCode); |
| } |
| } |
|
|