{"record":{"id":"136692f87a1b6a3f","repo":"Stirling-Tools/Stirling-PDF","slug":"response-body-is-null","errorCode":null,"errorMessage":"Response body is null","messagePattern":"Response body is null","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/proprietary/components/chat/ChatContext.tsx","lineNumber":332,"sourceCode":"interface ProgressEvent {\n  phase: string;\n  timestamp: number;\n  tool?: string;\n  stepIndex?: number;\n  stepCount?: number;\n  engineDetail?: AnyEngineProgressDetail;\n}\n\nasync function consumeSSEStream(\n  response: Response,\n  handlers: {\n    onProgress: (data: ProgressEvent) => void;\n    onResult: (data: AiWorkflowResponse) => void;\n    onError: (data: { message: string }) => void;\n  },\n) {\n  if (!response.body) {\n    throw new Error(\"Response body is null\");\n  }\n  const reader = response.body.getReader();\n  const decoder = new TextDecoder();\n  let buffer = \"\";\n  let currentEvent = \"\";\n\n  try {\n    for (;;) {\n      const { done, value } = await reader.read();\n      if (done) break;\n      buffer += decoder.decode(value, { stream: true });\n\n      // SSE frames are separated by double newlines\n      let boundary = buffer.indexOf(\"\\n\\n\");\n      while (boundary !== -1) {\n        const frame = buffer.slice(0, boundary);\n        buffer = buffer.slice(boundary + 2);\n","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/proprietary/components/chat/ChatContext.tsx#L314-L350","documentation":"Thrown by consumeSSEStream when response.body is null/undefined. The Fetch API sets response.body to a ReadableStream only when the response is a streaming body; if the request was opaque (no-cors), a 204, or the body was already consumed, response.body is null and getReader() cannot be called.","triggerScenarios":"The AI orchestration endpoint returned a response with no body (e.g. 204 No Content, or a body already read by prior code), the fetch used a mode that yields an opaque response, or a service worker/proxy stripped the streaming body.","commonSituations":"The AI engine returned an empty 200/204 body due to an internal error. A misconfigured reverse proxy (nginx) buffered or dropped the streaming body. The response.json() was already awaited upstream, exhausting the body before consumeSSEStream is called.","solutions":["Check response.ok and content-type (text/event-stream) before calling consumeSSEStream; handle non-streaming responses separately.","Verify the backend endpoint actually returns a streaming body (not buffered by a proxy) — set proxy_buffering off and X-Accel-Buffering: no.","Ensure no upstream code reads response.body or calls response.json() before this function.","Provide a fallback error message when body is null instead of a raw throw."],"exampleFix":"// before\nif (!response.body) {\n  throw new Error(\"Response body is null\");\n}\n\n// after\nif (!response.body) {\n  throw new Error(\n    `AI engine returned an empty stream (status ${response.status}). The engine may be unavailable or a proxy may have buffered the response.`,\n  );\n}","handlingStrategy":"validation","validationCode":"// Validate the response is a stream before consuming\nif (!response.ok || !response.body) {\n  throw new Error(`AI engine returned no stream body (status ${response.status}).`);\n}\nconst contentType = response.headers.get(\"content-type\") ?? \"\";\nif (!contentType.includes(\"event-stream\")) {\n  throw new Error(`Expected text/event-stream, got ${contentType}.`);\n}","typeGuard":"function isStreamingResponse(res: Response): boolean {\n  return !!res.body && (res.headers.get(\"content-type\") ?? \"\").includes(\"event-stream\");\n}","tryCatchPattern":null,"preventionTips":["Confirm the endpoint returns text/event-stream and that proxies do not buffer it.","Never consume response.json() before handing the response to the stream reader.","Surface a descriptive error including status and content-type."],"tags":["network","sse","fetch-api","streaming","ai-engine"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}