refactor(core): improve API response error logging when retry (#21784)

This commit is contained in:
Yuna Seol
2026-03-10 10:29:35 -04:00
committed by GitHub
parent f9fc83089c
commit 0486a1675a
3 changed files with 14 additions and 19 deletions

View File

@@ -1154,6 +1154,7 @@ describe('GeminiChat', () => {
1, 1,
); );
expect(mockLogContentRetry).not.toHaveBeenCalled(); expect(mockLogContentRetry).not.toHaveBeenCalled();
expect(mockLogContentRetryFailure).toHaveBeenCalledTimes(1);
}); });
it('should yield a RETRY event when an invalid stream is encountered', async () => { it('should yield a RETRY event when an invalid stream is encountered', async () => {

View File

@@ -344,8 +344,6 @@ export class GeminiChat {
this: GeminiChat, this: GeminiChat,
): AsyncGenerator<StreamEvent, void, void> { ): AsyncGenerator<StreamEvent, void, void> {
try { try {
let lastError: unknown = new Error('Request failed after all retries.');
const maxAttempts = INVALID_CONTENT_RETRY_OPTIONS.maxAttempts; const maxAttempts = INVALID_CONTENT_RETRY_OPTIONS.maxAttempts;
for (let attempt = 0; attempt < maxAttempts; attempt++) { for (let attempt = 0; attempt < maxAttempts; attempt++) {
@@ -374,15 +372,13 @@ export class GeminiChat {
yield { type: StreamEventType.CHUNK, value: chunk }; yield { type: StreamEventType.CHUNK, value: chunk };
} }
lastError = null; return;
break;
} catch (error) { } catch (error) {
if (error instanceof AgentExecutionStoppedError) { if (error instanceof AgentExecutionStoppedError) {
yield { yield {
type: StreamEventType.AGENT_EXECUTION_STOPPED, type: StreamEventType.AGENT_EXECUTION_STOPPED,
reason: error.reason, reason: error.reason,
}; };
lastError = null; // Clear error as this is an expected stop
return; // Stop the generator return; // Stop the generator
} }
@@ -397,7 +393,6 @@ export class GeminiChat {
value: error.syntheticResponse, value: error.syntheticResponse,
}; };
} }
lastError = null; // Clear error as this is an expected stop
return; // Stop the generator return; // Stop the generator
} }
@@ -415,8 +410,9 @@ export class GeminiChat {
} }
// Fall through to retry logic for retryable connection errors // Fall through to retry logic for retryable connection errors
} }
lastError = error;
const isContentError = error instanceof InvalidStreamError; const isContentError = error instanceof InvalidStreamError;
const errorType = isContentError ? error.type : 'NETWORK_ERROR';
if ( if (
(isContentError && isGemini2Model(model)) || (isContentError && isGemini2Model(model)) ||
@@ -425,11 +421,10 @@ export class GeminiChat {
// Check if we have more attempts left. // Check if we have more attempts left.
if (attempt < maxAttempts - 1) { if (attempt < maxAttempts - 1) {
const delayMs = INVALID_CONTENT_RETRY_OPTIONS.initialDelayMs; const delayMs = INVALID_CONTENT_RETRY_OPTIONS.initialDelayMs;
const retryType = isContentError ? error.type : 'NETWORK_ERROR';
logContentRetry( logContentRetry(
this.config, this.config,
new ContentRetryEvent(attempt, retryType, delayMs, model), new ContentRetryEvent(attempt, errorType, delayMs, model),
); );
coreEvents.emitRetryAttempt({ coreEvents.emitRetryAttempt({
attempt: attempt + 1, attempt: attempt + 1,
@@ -444,21 +439,19 @@ export class GeminiChat {
continue; continue;
} }
} }
break;
}
}
if (lastError) { // If we've aborted, we throw without logging a failure.
if ( if (signal.aborted) {
lastError instanceof InvalidStreamError && throw error;
isGemini2Model(model) }
) {
logContentRetryFailure( logContentRetryFailure(
this.config, this.config,
new ContentRetryFailureEvent(maxAttempts, lastError.type, model), new ContentRetryFailureEvent(attempt + 1, errorType, model),
); );
throw error;
} }
throw lastError;
} }
} finally { } finally {
streamDoneResolver!(); streamDoneResolver!();

View File

@@ -401,6 +401,7 @@ describe('GeminiChat Network Retries', () => {
// Should only be called once (no retry) // Should only be called once (no retry)
expect(mockContentGenerator.generateContentStream).toHaveBeenCalledTimes(1); expect(mockContentGenerator.generateContentStream).toHaveBeenCalledTimes(1);
expect(mockLogContentRetryFailure).not.toHaveBeenCalled();
}); });
it('should retry on SSL error during stream iteration (mid-stream failure)', async () => { it('should retry on SSL error during stream iteration (mid-stream failure)', async () => {