can1357/oh-my-pi · error · AIError.ProviderResponseError
Google API stream ended without a finish reason (connection
Error message
Google API stream ended without a finish reason (connection dropped or response truncated)
What it means
consumeGoogleStream reached end-of-stream without ever seeing a candidate finishReason, meaning the SSE stream from Google terminated abnormally. The library throws AIError.ProviderResponseError with kind "incomplete-stream" because a complete Google response always ends with a finish reason.
Source
Thrown at packages/ai/src/providers/google-shared.ts:767
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
total: 0,
},
};
calculateCost(model, output.usage);
}
}
flushCurrent();
if (options?.signal?.aborted) {
throw new AIError.AbortError();
}
if (!sawFinishReason) {
throw new AIError.ProviderResponseError(
"Google API stream ended without a finish reason (connection dropped or response truncated)",
{ provider: model.provider, kind: "incomplete-stream" },
);
}
if (output.stopReason === "aborted" || output.stopReason === "error") {
throw new AIError.ProviderResponseError(output.errorMessage ?? "An unknown error occurred", {
provider: model.provider,
kind: "output",
});
}
}
/**
* Generation/sampling fields that map directly onto Gemini's `GenerateContentConfig`.
* Excludes any provider-specific extensions (`topP`/`topK`/etc are all forwarded as-is).
*/
interface GoogleGenerationConfig extends GenerateContentConfig {View on GitHub (pinned to 9690622007)
Solutions
- Implement retry with exponential backoff — these are usually transient network failures
- Catch AIError.ProviderResponseError with kind "incomplete-stream" and re-issue the request, keeping any partial output if acceptable
- Check network path: disable VPN/proxy or raise its stream timeout
- Reduce max output tokens so responses complete within connection lifetime
Example fix
// before
const result = await streamGoogle(model, params);
// after
async function withRetry() {
for (let attempt = 0; attempt < 3; attempt++) {
try {
return await streamGoogle(model, params);
} catch (err) {
if (err instanceof AIError.ProviderResponseError && err.context?.kind === "incomplete-stream" && attempt < 2) {
await Bun.sleep(2 ** attempt * 500);
continue;
}
throw err;
}
}
} Defensive patterns
Strategy: retry
Validate before calling
null
Type guard
null
Try / catch
try {
return await streamGoogle(model, params, { signal });
} catch (err) {
if (err instanceof AIError.ProviderResponseError && err.context?.kind === "incomplete-stream" && !signal.aborted) {
return await withBackoff(() => streamGoogle(model, params, { signal }), 3);
}
throw err;
} Prevention
- Always wrap streaming calls in retry-with-backoff; drops are transient
- Use keep-alive aware HTTP agents and avoid proxies with aggressive idle timeouts
- Cap maxOutputTokens to short enough generations that connections survive
- Handle partial output gracefully: accumulate chunks so a retry can resume or discard cleanly
When it happens
Trigger: The HTTP connection to Google dropped mid-stream; a proxy/load-balancer cut the SSE connection; server-side crash mid-generation; network interruption; response truncated by infrastructure timeout.
Common situations: Long generations over flaky networks; corporate proxies with idle/stream timeouts; Cloud Run / API gateway response limits; transient Google infrastructure errors.
Related errors
- V2 compaction stream closed before response.completed
- Google API returned an empty response body
- stream ended before message_start
- stream ended before message_stop
- Devin API error: response body is empty
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/f7cc0dd7a94cbfd1.
Report an issue: GitHub.