can1357/oh-my-pi · error · AnthropicStreamEnvelopeError
Attempted to iterate over an Anthropic response with no body
Error message
Attempted to iterate over an Anthropic response with no body
What it means
This AnthropicStreamEnvelopeError is thrown when the code tries to consume a streaming response whose body is null. SSE streaming requires a readable body; a null body means there is no stream to parse, so the library fails immediately rather than yielding zero events silently.
Source
Thrown at packages/ai/src/providers/anthropic.ts:1430
if (message) {
return new AIError.ProviderResponseError(
errorType ? `Anthropic stream error (${errorType}): ${message}` : `Anthropic stream error: ${message}`,
{ provider: "anthropic", kind: "output" },
);
}
} catch {
// Not a JSON envelope; fall through to the raw payload.
}
return new AIError.ProviderResponseError(data, { provider: "anthropic", kind: "output" });
}
async function* iterateAnthropicEvents(
response: Response,
signal?: AbortSignal,
onSseEvent?: AnthropicOptions["onSseEvent"],
): AsyncGenerator<AnthropicStreamEvent> {
if (!response.body) {
throw new AIError.AnthropicStreamEnvelopeError("Attempted to iterate over an Anthropic response with no body");
}
let sawMessageStart = false;
let sawMessageEnd = false;
for await (const sse of readSseEvents(response.body, signal)) {
notifyRawSseEvent(onSseEvent, sse);
if (sse.event === "error") {
throw createAnthropicSseStreamError(sse.data);
}
if (sse.event === "ping") {
// Surface keepalives so the idle watchdog treats them as liveness.
yield ANTHROPIC_PING_EVENT;
continue;
}
if (!ANTHROPIC_MESSAGE_EVENTS.has(sse.event ?? "")) {View on GitHub (pinned to 9690622007)
Solutions
- Ensure the fetch/API call used streaming semantics so the response carries a readable body.
- If mocking, supply a ReadableStream body (e.g. a Response with an SSE text stream).
- Audit custom fetch wrappers/proxies for body consumption or removal before the Response is returned.
- Check for earlier code that called response.json()/text() on the same Response, which locks/detaches the body.
Example fix
// before
const res = new Response(); // body: null
yield* iterateAnthropicEvents(res);
// after
const sse = 'event: message_start\ndata: {"type":"message_start"}\n\n';
const res = new Response(sse, { headers: { "content-type": "text/event-stream" } });
yield* iterateAnthropicEvents(res); Defensive patterns
Strategy: type-guard
Validate before calling
if (!response.body) {
throw new Error("Streaming response has no body; check that the request used streaming and the fetch wrapper preserves the body.");
} Type guard
function hasReadableBody(response: Response): response is Response & { body: ReadableStream<Uint8Array> } {
return response.body !== null;
} Try / catch
try {
for await (const event of iterateAnthropicEvents(response, signal)) {
handle(event);
}
} catch (err) {
if (err instanceof AIError.AnthropicStreamEnvelopeError && /no body/.test(err.message)) {
// fall back to a fresh non-streaming request or re-issue the streaming call
} else {
throw err;
}
} Prevention
- Never consume (json()/text()) a Response you intend to stream later.
- In mocks, always construct Response objects with a real ReadableStream body.
- Audit custom fetch wrappers to ensure they return the original Response untouched.
- Check status codes before streaming — bodiless statuses (204, some 3xx) cannot carry SSE.
When it happens
Trigger: Passing a Response object with a null body into iterateAnthropicEvents — e.g. a response constructed manually, a mock, a 204/bodiless response, or a runtime that already consumed or detached the body.
Common situations: Test mocks returning `new Response()` with no body; proxy/gateway stripping the body; calling the streaming path with a non-streaming response object; a custom fetch wrapper that reads the body before returning the Response.
Related errors
- received ${event.type} before message_start
- V2 compaction stream closed before response.completed
- V2 compaction stream parse failed: ${err instanceof Error ?
- Auth broker stream did not start with snapshot
- Auth broker stream ended unexpectedly
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/79577f2bd94068d8.
Report an issue: GitHub.