can1357/oh-my-pi · error · AIError.ProviderResponseError
OpenAI stream response has no body (status ${response.status
Error message
OpenAI stream response has no body (status ${response.status}) What it means
AIError.ProviderResponseError thrown by postOpenAIStream when the OpenAI-compatible endpoint answered with an OK status but response.body is null/undefined, so SSE parsing cannot begin. This normally only happens with non-streaming-capable fetch implementations or interception layers that replace the body. It is thrown with kind "envelope" since the HTTP envelope itself is defective.
Source
Thrown at packages/ai/src/utils/openai-http.ts:111
headers: { "Content-Type": "application/json", Accept: "text/event-stream", ...init.headers },
body: JSON.stringify(init.body),
signal: init.signal,
fetch: init.fetch,
maxAttempts: DEFAULT_MAX_ATTEMPTS,
// A proxy concurrency-admission 429 (`rate_limit_type: max_parallel_requests`)
// surfaces immediately instead of being slept-and-retried here; session
// recovery owns its backoff/fallback (issue #8854).
shouldRetryResponse: (response, bodyText) => !isConcurrencyAdmissionRejection(response, bodyText),
// Bun's native fetch enforces a hard ~300s pre-response timeout (issue #2422).
// Cold large-context streams legitimately exceed it; the caller's
// `firstEventTimeoutMs`/`AbortSignal` already govern stuck requests.
timeout: false,
});
if (!response.ok) {
throw await captureOpenAIHttpError(response);
}
if (!response.body) {
throw new AIError.ProviderResponseError(`OpenAI stream response has no body (status ${response.status})`, {
kind: "envelope",
});
}
return {
events: readSseJson<TEvent>(response.body, init.signal, init.onSseEvent),
response,
requestId: response.headers.get("x-request-id"),
};
}
/** Decode a non-2xx response into an {@link OpenAIHttpError} without consuming it twice. */
export async function captureOpenAIHttpError(response: Response): Promise<AIError.OpenAIHttpError> {
let bodyText: string | undefined;
let bodyJson: unknown;
try {
bodyText = await response.text();
if (bodyText.trim().length > 0) {
try {View on GitHub (pinned to 9690622007)
Solutions
- Inspect any custom fetch implementation passed to the client — ensure it returns a Response with a live readable body.
- Fix test mocks to construct `new Response(readableStream)` instead of a body-less Response.
- Remove caching/logging wrappers that consume response.body before returning it.
- If the server legitimately sends an empty 200 (shouldn't for streams), check the endpoint actually supports `stream: true`.
Example fix
// before: mock strips body
fetch: async () => new Response(null, { status: 200 });
// after: mock provides a readable SSE body
fetch: async () => new Response(sseStreamOf(chunk), { status: 200, headers: { "content-type": "text/event-stream" } }); Defensive patterns
Strategy: validation
Validate before calling
// when injecting a custom fetch (tests/proxies), assert body presence before use
const res = await fetch(url, init);
if (res.ok && !res.body) throw new Error("custom fetch returned 200 without a body"); Type guard
function hasBody(res: Response): res is Response & { body: ReadableStream<Uint8Array> } {
return res.body !== null;
} Try / catch
try {
const stream = await client.stream(request);
} catch (err) {
if (err instanceof AIError.ProviderResponseError && /no body/.test(err.message)) {
// fix custom fetch shim / retry with default fetch
} else throw err;
} Prevention
- Never return body-less Responses from custom fetch shims or test mocks.
- Avoid wrappers that consume response.body before returning it.
- Verify streaming endpoints are called with stream: true so a body is always produced.
When it happens
Trigger: A successful (response.ok) streaming POST where response.body is falsy — custom ctx.fetch shims that return a Response without a body, some polyfills/test mocks, or node-fetch-style environments where body was already consumed or not provided.
Common situations: Unit tests injecting mock Responses built without a body; custom fetch wrappers (logging/caching) that read and fail to re-attach the body; runtime polyfill mismatches (e.g. undici mock agents returning opaque responses).
Related errors
- V2 remote compaction failed (${response.status} ${response.s
- No response body for V2 compaction streaming
- formatCompactionV2Failure(event, type)
- Bedrock response has no body
- OpenAI Files API returned an invalid upload response
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/5c538e8ab43d9b75.
Report an issue: GitHub.