{"record":{"id":"460ab8dc288af50b","repo":"moeru-ai/airi","slug":"streaming-transcription-response-is-missing-a-read","errorCode":null,"errorMessage":"Streaming transcription response is missing a readable body.","messagePattern":"Streaming transcription response is missing a readable body\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-ui/src/libs/providers/stream-transcription/index.ts","lineNumber":157,"sourceCode":"      const requestTarget = options.baseURL instanceof URL\n        ? options.baseURL\n        : new URL(typeof options.baseURL === 'string' ? options.baseURL : 'http://localhost')\n      const response = await fetcher(requestTarget, {\n        body: audioStream,\n        // Browser fetch requires half-duplex mode for a ReadableStream body.\n        // Keep this at the transport boundary so every SSE transcription\n        // provider receives the required request option.\n        duplex: 'half',\n        headers: options.headers,\n        method: 'POST',\n        signal: options.abortSignal,\n      } as RequestInit & { duplex: 'half' })\n\n      if (!response.ok)\n        throw new Error(`Streaming transcription request failed with status ${response.status}`)\n\n      if (!response.body)\n        throw new Error('Streaming transcription response is missing a readable body.')\n\n      await response.body\n        .pipeThrough(createSSETransformer())\n        .pipeTo(new WritableStream<AIRIStreamTranscriptionDelta>({\n          write: (chunk) => {\n            fullStreamCtrl?.enqueue(chunk)\n            if (chunk.type === 'transcript.text.delta') {\n              text += chunk.delta\n              textStreamCtrl?.enqueue(chunk.delta)\n            }\n            else if (chunk.type === 'transcript.text.snapshot') {\n              text = chunk.text\n            }\n          },\n          close: () => {\n            fullStreamCtrl?.close()\n            textStreamCtrl?.close()\n          },","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/stage-ui/src/libs/providers/stream-transcription/index.ts#L139-L175","documentation":"Thrown by the shared streaming-transcription helper when the upload POST returned 2xx but `response.body` is null, so there is no SSE stream to pipe through `createSSETransformer()`. The response must be a streaming body — a body-less success means the transport contract for server-sent events is broken before any transcript delta can arrive.","triggerScenarios":"Intermediary (proxy, service worker, antivirus TLS inspection) converting the streamed response into a buffered, body-less one; runtime fetch lacking ReadableStream response support; server answering 204/HEAD-like empty responses on success; test environment mocking fetch without bodies.","commonSituations":"Corporate proxy stripping SSE; happy-path unit test with a stubbed fetch returning `{ ok: true, body: undefined }`; Electron main-process fetch shim without stream plumbing; misconfigured CDN buffering `/transcriptions` endpoints.","solutions":["Confirm the backend really streams SSE for this endpoint (curl -N and watch for events).","Bypass or reconfigure proxies/CDNs for the transcription route (disable buffering, preserve transfer-encoding).","In tests, make fetch stubs return a real `ReadableStream` body.","If the environment cannot stream, use a buffered (non-streaming) transcription call instead of this helper."],"exampleFix":"// before\nif (!response.body)\n  throw new Error('Streaming transcription response is missing a readable body.')\n// after — combine with the status check and name the likely culprit\nif (!response.ok || !response.body)\n  throw new Error(`Streaming transcription unusable response: status=${response.status} body=${response.body ? 'present' : 'missing — SSE stripped by proxy/runtime?'}`)","handlingStrategy":"type-guard","validationCode":"// pre-flight the runtime before first use\nif (typeof ReadableStream === 'undefined')\n  throw new Error('This runtime cannot consume SSE transcription — use buffered mode')","typeGuard":"function isStreamingResponse(res: Response): res is Response & { body: ReadableStream<Uint8Array> } {\n  return res.ok && res.body instanceof ReadableStream\n}","tryCatchPattern":"if (!isStreamingResponse(response))\n  // degrade to a buffered transcription call rather than piping\n  return bufferedTranscribe(audioStream)","preventionTips":["Ensure the runtime's fetch exposes response streams.","Keep SSE routes excluded from buffering proxies/CDNs.","Mock fetch in tests with real ReadableStream bodies.","Offer a buffered transcription fallback for restricted environments."],"tags":["network","streaming","sse","transcription","fetch"],"backgroundTag":"empty-response-body","analyzedSha":"677329427f32468c74b17f3ec47eeca4e05bec65","analyzedAt":"2026-08-18T17:29:58.153Z","contentChangedAt":"2026-08-18T17:29:58.153Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}