can1357/oh-my-pi · error · AIError.ProviderResponseError
Google API returned an empty response body
Error message
Google API returned an empty response body
What it means
streamGoogleGenAI received a response with ok status but a null/undefined body, so there is no SSE stream to consume. The library throws AIError.ProviderResponseError with kind "empty-body" because a successful streaming response always has a body.
Source
Thrown at packages/ai/src/providers/google-shared.ts:990
const bodyJson = JSON.stringify(paramsToWireBody(params));
const fetchImpl = plan.fetch ?? options?.fetch ?? (globalThis.fetch.bind(globalThis) as FetchImpl);
const openStreamAt = async (requestUrl: string): Promise<ReadableStream<Uint8Array>> => {
const response = await fetchImpl(requestUrl, {
method: "POST",
headers: { ...plan.headers, "Content-Type": "application/json", Accept: "text/event-stream" },
body: bodyJson,
signal: options?.signal,
});
if (!response.ok) {
const errorText = await response.text().catch(() => "");
throw new AIError.GoogleApiError(
`Google API error (${response.status}): ${extractGoogleErrorMessage(errorText)}`,
response.status,
{ headers: response.headers },
);
}
if (!response.body) {
throw new AIError.ProviderResponseError("Google API returned an empty response body", {
provider: model.provider,
kind: "empty-body",
});
}
return response.body as ReadableStream<Uint8Array>;
};
// A regional Vertex endpoint 404s for models published only on the
// global endpoint; retry global once so a stale/ambient region never
// breaks a request that worked before regional routing existed.
const openStream = async (): Promise<ReadableStream<Uint8Array>> => {
if (!plan.fallbackUrl) return openStreamAt(plan.url);
try {
return await openStreamAt(plan.url);
} catch (error) {
if (error instanceof AIError.GoogleApiError && error.status === 404) {
return openStreamAt(plan.fallbackUrl);
}
throw error;View on GitHub (pinned to 9690622007)
Solutions
- If in tests, fix the fetch mock to return a ReadableStream body
- Retry the request — rare in production and usually transient
- Inspect proxies/middlewares between the client and Google that could drop the body
- Catch AIError.ProviderResponseError with kind "empty-body" and fall back to a non-streaming request
Example fix
// before
// test mock
fetch.mockResolvedValue({ ok: true, status: 200 });
// after
fetch.mockResolvedValue({
ok: true,
status: 200,
body: new ReadableStream({ start(c) { c.enqueue(new TextEncoder().encode('data: {...}\n\n')); c.close(); } }),
headers: new Headers({ "content-type": "text/event-stream" }),
}); Defensive patterns
Strategy: retry
Validate before calling
null
Type guard
function hasBody(res: Response): res is Response & { body: ReadableStream<Uint8Array> } {
return res.body !== null && res.body !== undefined;
} Try / catch
try {
return await streamGoogle(model, params, { signal });
} catch (err) {
if (err instanceof AIError.ProviderResponseError && err.context?.kind === "empty-body") {
return withBackoff(() => streamGoogle(model, params, { signal }), 2);
}
throw err;
} Prevention
- In tests, always provide a real ReadableStream body in fetch mocks for streaming endpoints
- Audit proxies/interceptors between client and Google for body-stripping behavior
- Add a single retry for empty-body responses — rare and usually transient
- Prefer non-streaming generateContent as a fallback when streaming repeatedly returns empty bodies
When it happens
Trigger: HTTP 200 with empty body — typically caused by an intermediary (proxy, service worker, mocked fetch in tests) stripping the body, or an edge node returning an empty success.
Common situations: Tests with incomplete fetch mocks (status set but body omitted); corporate proxies returning empty 200s; intermittent CDN/load-balancer bugs.
Related errors
- Google API stream ended without a finish reason (connection
- V2 compaction stream closed before response.completed
- 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/f64edccd37ce85ca.
Report an issue: GitHub.