{"record":{"id":"79577f2bd94068d8","repo":"can1357/oh-my-pi","slug":"attempted-to-iterate-over-an-anthropic-response-wi","errorCode":null,"errorMessage":"Attempted to iterate over an Anthropic response with no body","messagePattern":"Attempted to iterate over an Anthropic response with no body","errorType":"exception","errorClass":"AnthropicStreamEnvelopeError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/providers/anthropic.ts","lineNumber":1430,"sourceCode":"\t\tif (message) {\n\t\t\treturn new AIError.ProviderResponseError(\n\t\t\t\terrorType ? `Anthropic stream error (${errorType}): ${message}` : `Anthropic stream error: ${message}`,\n\t\t\t\t{ provider: \"anthropic\", kind: \"output\" },\n\t\t\t);\n\t\t}\n\t} catch {\n\t\t// Not a JSON envelope; fall through to the raw payload.\n\t}\n\treturn new AIError.ProviderResponseError(data, { provider: \"anthropic\", kind: \"output\" });\n}\n\nasync function* iterateAnthropicEvents(\n\tresponse: Response,\n\tsignal?: AbortSignal,\n\tonSseEvent?: AnthropicOptions[\"onSseEvent\"],\n): AsyncGenerator<AnthropicStreamEvent> {\n\tif (!response.body) {\n\t\tthrow new AIError.AnthropicStreamEnvelopeError(\"Attempted to iterate over an Anthropic response with no body\");\n\t}\n\n\tlet sawMessageStart = false;\n\tlet sawMessageEnd = false;\n\n\tfor await (const sse of readSseEvents(response.body, signal)) {\n\t\tnotifyRawSseEvent(onSseEvent, sse);\n\t\tif (sse.event === \"error\") {\n\t\t\tthrow createAnthropicSseStreamError(sse.data);\n\t\t}\n\n\t\tif (sse.event === \"ping\") {\n\t\t\t// Surface keepalives so the idle watchdog treats them as liveness.\n\t\t\tyield ANTHROPIC_PING_EVENT;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!ANTHROPIC_MESSAGE_EVENTS.has(sse.event ?? \"\")) {","sourceCodeStart":1412,"sourceCodeEnd":1448,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/providers/anthropic.ts#L1412-L1448","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst res = new Response(); // body: null\nyield* iterateAnthropicEvents(res);\n\n// after\nconst sse = 'event: message_start\\ndata: {\"type\":\"message_start\"}\\n\\n';\nconst res = new Response(sse, { headers: { \"content-type\": \"text/event-stream\" } });\nyield* iterateAnthropicEvents(res);","handlingStrategy":"type-guard","validationCode":"if (!response.body) {\n  throw new Error(\"Streaming response has no body; check that the request used streaming and the fetch wrapper preserves the body.\");\n}","typeGuard":"function hasReadableBody(response: Response): response is Response & { body: ReadableStream<Uint8Array> } {\n  return response.body !== null;\n}","tryCatchPattern":"try {\n  for await (const event of iterateAnthropicEvents(response, signal)) {\n    handle(event);\n  }\n} catch (err) {\n  if (err instanceof AIError.AnthropicStreamEnvelopeError && /no body/.test(err.message)) {\n    // fall back to a fresh non-streaming request or re-issue the streaming call\n  } else {\n    throw err;\n  }\n}","preventionTips":["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."],"tags":["streaming","sse","anthropic"],"backgroundTag":"empty-response-body","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}