moeru-ai/airi · error · Error

Streaming transcription response is missing a readable body.

Error message

Streaming transcription response is missing a readable body.

What it means

Thrown in streamTranscription's async pump when the transcription POST returns response.ok === true but response.body is null. The adapter must pipe response.body through an SSE transformer to produce transcript deltas; without a body it cannot emit any events and errors both stream controllers plus the text promise.

Source

Thrown at packages/stage-ui/src/libs/providers/stream-transcription/index.ts:135

  })

  void (async () => {
    try {
      const requestTarget = options.baseURL instanceof URL
        ? options.baseURL
        : new URL(typeof options.baseURL === 'string' ? options.baseURL : 'http://localhost')
      const response = await fetcher(requestTarget, {
        body: audioStream,
        headers: options.headers,
        method: 'POST',
        signal: options.abortSignal,
      })

      if (!response.ok)
        throw new Error(`Streaming transcription request failed with status ${response.status}`)

      if (!response.body)
        throw new Error('Streaming transcription response is missing a readable body.')

      await response.body
        .pipeThrough(createSSETransformer())
        .pipeTo(new WritableStream<AIRIStreamTranscriptionDelta>({
          write: (chunk) => {
            fullStreamCtrl?.enqueue(chunk)
            if (chunk.type === 'transcript.text.delta') {
              text += chunk.delta
              textStreamCtrl?.enqueue(chunk.delta)
            }
            else if (chunk.type === 'transcript.text.snapshot') {
              text = chunk.text
            }
          },
          close: () => {
            fullStreamCtrl?.close()
            textStreamCtrl?.close()
          },

View on GitHub (pinned to 27111382b4)

Solutions

  1. Ensure the runtime's fetch exposes streaming bodies on Response (Node 18+ global fetch, modern browsers).
  2. Check proxies/load balancers between client and transcription server do not buffer SSE; they must preserve chunked streaming.
  3. Treat a null body on a 2xx as a transport incompatibility and surface it distinctly from a server error.
  4. Retry once in case of an intermittent upstream empty-body response.

Example fix

// before
if (!response.body)
  throw new Error('Streaming transcription response is missing a readable body.')

// after: include status to aid diagnosis
if (!response.body)
  throw new Error(
    `Streaming transcription returned 2xx (status=${response.status}) but no readable body; ` +
    `verify the transport preserves SSE streaming.`,
  )
Defensive patterns

Strategy: validation

Validate before calling

function supportsSSEStreaming(): boolean {
  return typeof ReadableStream !== 'undefined'
    && typeof TransformStream !== 'undefined'
    && typeof globalThis.fetch === 'function'
}

Type guard

function hasStreamingTranscriptionBody(response: Response): response is Response & { body: ReadableStream<Uint8Array> } {
  return response.ok && response.body instanceof ReadableStream
}

Try / catch

const result = streamTranscription(options)
try {
  const text = await result.text
  // use text
}
catch (error) {
  if (String(error).includes('no readable body'))
    reportTransportIncompatibility(error)
  throw error
}

Prevention

When it happens

Trigger: A 2xx transcription response whose body is not exposed as a ReadableStream: non-streaming fetch polyfill, a proxy that buffers the SSE stream into a single buffered response, or an intermediary that consumed/locked the body before the adapter reads it.

Common situations: Running under a fetch shim lacking streaming bodies; a reverse proxy converting Transfer-Encoding: chunked into Content-Length buffered responses; an upstream that returns 200 with empty body on partial failures; Node versions without web streams enabled.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/460ab8dc288af50b. Report an issue: GitHub.