From f10aaf7e7e063ffa29832223a241623a1c84333e Mon Sep 17 00:00:00 2001 From: Allen Hutchison Date: Wed, 16 Apr 2025 16:53:56 -0700 Subject: [PATCH] fix: Suppress crash from unhandled 429 stream error via global handler Introduces a process.on('unhandledRejection') handler in src/gemini.ts as a workaround for an issue where 429 ClientErrors originating from the @google/genai library's sendMessageStream during iteration can cause an unhandled rejection, even when caught within local try/catch blocks in the application code (e.g., in processGeminiStream). The handler specifically identifies this known 429 ClientError based on its type and message content. If matched, it logs a warning indicating the known issue is being suppressed and prevents process.exit(1). Any other genuinely unhandled promise rejections will still be logged as critical errors and will terminate the application, maintaining default behavior for unexpected issues. This workaround mitigates a suspected library-internal problem related to error propagation during asynchronous stream iteration. --- packages/cli/src/gemini.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/packages/cli/src/gemini.ts b/packages/cli/src/gemini.ts index 449f5096..96d70f01 100644 --- a/packages/cli/src/gemini.ts +++ b/packages/cli/src/gemini.ts @@ -26,6 +26,39 @@ async function main() { render(React.createElement(App, { directory: targetDir })); } +// --- Global Unhandled Rejection Handler --- +process.on('unhandledRejection', (reason, promise) => { + // Check if this is the known 429 ClientError that sometimes escapes + // this is a workaround for a specific issue with the way we are calling gemini + // where a 429 error is thrown but not caught, causing an unhandled rejection + // TODO(adh): Remove this when the race condition is fixed + const isKnownEscaped429 = + reason instanceof Error && + reason.name === 'ClientError' && + reason.message.includes('got status: 429'); + + if (isKnownEscaped429) { + // Log it differently and DON'T exit, as it's likely already handled visually + console.warn('-----------------------------------------'); + console.warn('WORKAROUND: Suppressed known escaped 429 Unhandled Rejection.'); + console.warn('-----------------------------------------'); + console.warn('Reason:', reason); + // No process.exit(1); + } else { + // Log other unexpected unhandled rejections as critical errors + console.error('========================================='); + console.error('CRITICAL: Unhandled Promise Rejection!'); + console.error('========================================='); + console.error('Reason:', reason); + console.error('Stack trace may follow:'); + if (!(reason instanceof Error)) { + console.error(reason); + } + // Exit for genuinely unhandled errors + process.exit(1); + } +}); + // --- Global Entry Point --- main().catch((error) => { console.error('An unexpected critical error occurred:');