{"record":{"id":"2bd0fa812d52d72f","repo":"moeru-ai/airi","slug":"streaming-transcription-request-failed-with-status","errorCode":null,"errorMessage":"Streaming transcription request failed with status ${response.status}","messagePattern":"Streaming transcription request failed with status (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/stage-ui/src/libs/providers/stream-transcription/index.ts","lineNumber":154,"sourceCode":"\n  void (async () => {\n    try {\n      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: () => {","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/moeru-ai/airi/blob/677329427f32468c74b17f3ec47eeca4e05bec65/packages/stage-ui/src/libs/providers/stream-transcription/index.ts#L136-L172","documentation":"Thrown by the shared streaming-transcription helper when the POST that uploads the raw audio ReadableStream (fetch with `duplex: 'half'`) returns a non-2xx status. The helper is provider-generic: `requestTarget`, headers, and `abortSignal` all come from the caller's options, so the status reflects whichever SSE transcription backend was configured.","triggerScenarios":"401/403 when the provider-specific auth header is missing/expired; 400 for an unsupported `Content-Type` on the audio stream; 413 when the audio stream exceeds the provider limit; 404 from a wrong `requestTarget` URL; 5xx upstream outage — all before any SSE parsing begins.","commonSituations":"Provider base URL typo'd in settings; audio sent with a content type the backend rejects (e.g. raw PCM labeled `audio/webm`); browser fetch rejecting the stream body silently behind a proxy that answers 4xx; token refresh racing the upload start.","solutions":["Log `response.status` from the message and map it: 401/403 → fix the provider credentials/headers; 400 → correct the audio `Content-Type`; 413 → shorten or transcode the audio; 404 → fix the request URL.","curl the same target with a small audio file and the same headers to reproduce outside the app.","Verify the provider endpoint actually speaks the expected SSE transcription protocol.","If the status is transient (429/5xx), retry with backoff before surfacing the failure."],"exampleFix":"// before\nif (!response.ok)\n  throw new Error(`Streaming transcription request failed with status ${response.status}`)\n// after — include the upstream body so the cause is diagnosable\nif (!response.ok) {\n  const detail = await response.text().catch(() => '')\n  throw new Error(`Streaming transcription request failed with status ${response.status}${detail ? `: ${detail.slice(0, 256)}` : ''}`)\n}","handlingStrategy":"try-catch","validationCode":"options.abortSignal?.throwIfAborted()\nif (!options.headers?.Authorization && !options.headers?.['x-api-key'])\n  throw new Error('Transcription provider credentials are required')","typeGuard":null,"tryCatchPattern":"try {\n  await startStreamingTranscription(options)\n}\ncatch (error) {\n  const status = /status (\\d{3})/.exec(errorMessageFrom(error))?.[1]\n  if (status === '401' || status === '403')\n    // refresh provider credentials\n  else if (status === '429' || status === '5xx')\n    // backoff + retry with the same abort signal\n  else\n    throw error\n}","preventionTips":["Validate the provider endpoint URL and required headers before opening the duplex stream.","Send an accurate audio Content-Type for the stream.","Cap audio length to avoid 413 rejections.","Retry transient statuses once with backoff before surfacing."],"tags":["network","api","streaming","transcription","fetch"],"backgroundTag":"upstream-api-error","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"}