{"record":{"id":"5987c9e1c3b79eb3","repo":"mastra-ai/mastra","slug":"mastra-livekit-mastra-agent-stream-returned-an-e","errorCode":null,"errorMessage":"@mastra/livekit: Mastra agent stream returned an empty response body","messagePattern":"@mastra/livekit: Mastra agent stream returned an empty response body","errorType":"exception","errorClass":"APIConnectionError","httpStatus":null,"severity":"error","filePath":"integrations/livekit/src/remote.ts","lineNumber":267,"sourceCode":"              if (cancelled) {\n                clearWatchdog();\n                break;\n              }\n              const response = await fetchImpl(url, {\n                method: 'POST',\n                headers: { 'content-type': 'application/json', accept: 'text/event-stream', ...resolvedHeaders },\n                body: JSON.stringify(requestBody),\n                signal: abortController.signal,\n              });\n              if (!response.ok) {\n                const errorBody = await safeReadBody(response);\n                throw new APIStatusError({\n                  message: `@mastra/livekit: Mastra agent stream request failed with status ${response.status}`,\n                  options: { statusCode: response.status, body: errorBody, retryable },\n                });\n              }\n              if (!response.body) {\n                throw new APIConnectionError({\n                  message: '@mastra/livekit: Mastra agent stream returned an empty response body',\n                  options: { retryable },\n                });\n              }\n\n              for await (const chunk of readMastraSSE(\n                response.body as unknown as globalThis.ReadableStream<Uint8Array>,\n                abortController.signal,\n              )) {\n                if (cancelled) break;\n                // First chunk: the server has committed to this generation — forbid any further\n                // retry so the turn can't be replayed mid-stream. The watchdog is NOT cleared here:\n                // lifecycle metadata (step-start, text-start, ...) isn't proof the model is\n                // producing anything, and disarming on it would turn a post-metadata stall into\n                // indefinite dead air. It disarms on the first sign of model output below.\n                retryable = false;\n                const payload = chunk.payload ?? {};\n                switch (chunk.type) {","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/integrations/livekit/src/remote.ts#L249-L285","documentation":"The HTTP response from the Mastra agent stream endpoint was ok but had a null/empty body, so there is no SSE stream to read. The library throws APIConnectionError (flagged retryable) because a body-less 200 response cannot yield any reply chunks.","triggerScenarios":"The fetch to /agents/:agentId/stream resolves with response.ok true but response.body null/empty — typically caused by a proxy, gateway, or malformed server response stripping the streaming body.","commonSituations":"Reverse proxies or load balancers buffering/dropping SSE responses; server closing the connection immediately; middleware consuming the response body before it is returned.","solutions":["Retry the request — the error is marked retryable since the failure is transient/transport-level.","Check any proxy/gateway between the worker and the Mastra server for SSE buffering or body stripping (disable buffering, allow streaming responses).","Confirm the Mastra server actually streams SSE for this route (test with curl -N).","Inspect Mastra server middleware that may consume or terminate the response body."],"exampleFix":"// before (nginx)\nproxy_buffering on;\n// after (nginx)\nproxy_buffering off;\nproxy_cache off; # allow SSE streaming through to the client","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await streamReply();\n} catch (e) {\n  if (e instanceof APIConnectionError && e.message.includes('empty response body')) {\n    // transport-level; safe to retry with backoff\n    await sleep(backoff(attempt));\n    return streamReply();\n  }\n  throw e;\n}","preventionTips":["Disable proxy/gateway response buffering for SSE routes.","Test the endpoint with `curl -N` to confirm chunked SSE streaming end to end.","Audit server middleware for code that consumes or terminates response bodies.","Set reasonable retry limits since this error is marked retryable."],"tags":["network","streaming","sse","livekit","proxy"],"backgroundTag":"empty-response-body","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}