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.
This commit is contained in:
Allen Hutchison 2025-04-16 16:53:56 -07:00 committed by Taylor Mullen
parent 123c3050dc
commit f10aaf7e7e
1 changed files with 33 additions and 0 deletions

View File

@ -26,6 +26,39 @@ async function main() {
render(React.createElement(App, { directory: targetDir })); 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 --- // --- Global Entry Point ---
main().catch((error) => { main().catch((error) => {
console.error('An unexpected critical error occurred:'); console.error('An unexpected critical error occurred:');