moeru-ai/airi · error · Error
Streaming transcription request failed with status ${respons
Error message
Streaming transcription request failed with status ${response.status} What it means
Thrown inside streamTranscription's async pump when the POST to the transcription baseURL returns a non-ok status. The adapter streams the audio body and expects an SSE response; on failure it aborts before piping, erroring both the fullStream and textStream controllers and rejecting the text promise. The HTTP status is included so the caller can distinguish auth vs server errors.
Source
Thrown at packages/stage-ui/src/libs/providers/stream-transcription/index.ts:132
start(controller) {
textStreamCtrl = controller
},
})
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: () => {View on GitHub (pinned to 27111382b4)
Solutions
- Confirm options.baseURL is the correct transcription endpoint and options.headers include a valid Authorization header.
- Map the embedded status: 401/403 -> re-authenticate; 404 -> fix baseURL; 5xx -> retry with backoff or report upstream outage.
- Ensure the audio stream encoding matches what the server expects (set Content-Type in options.headers appropriately).
- Handle the rejection on result.text / result.fullStream and surface a user-facing 'transcription unavailable' state.
Example fix
// caller-side handling of the async failure
const result = streamTranscription({ baseURL, headers, inputAudioStream })
try {
const transcript = await result.text
// use transcript
}
catch (error) {
console.error('Transcription failed:', error)
// show 'speech recognition unavailable' in the UI
} Defensive patterns
Strategy: try-catch
Validate before calling
function validateTranscriptionRequest(options: StreamTranscriptionOptions): string | null {
if (!options.baseURL)
return 'baseURL is required for transcription'
try {
new URL(typeof options.baseURL === 'string' ? options.baseURL : 'http://localhost')
}
catch {
return `baseURL is not a valid URL: ${options.baseURL}`
}
if (!options.headers || !(options.headers as Headers).get?.('Authorization')
&& !(Array.isArray(options.headers) && options.headers.some(([k]) => k.toLowerCase() === 'authorization')))
return 'Authorization header is missing; transcription will return 401'
return null
} Type guard
function isTranscriptionHttpError(error: unknown): boolean {
return error instanceof Error && /Streaming transcription request failed with status \d{3}/.test(error.message)
}
function extractStatus(error: unknown): number | undefined {
const match = /status (\d{3})/.exec(String((error as Error)?.message ?? ''))
return match ? Number(match[1]) : undefined
} Try / catch
const result = streamTranscription(options)
try {
for await (const chunk of result.fullStream) {
if (chunk.type === 'transcript.text.delta')
onDelta(chunk.delta)
}
}
catch (error) {
const status = extractStatus(error)
if (status && (status === 429 || status >= 500)) {
// transient; inform user and optionally retry
}
else if (status === 401 || status === 403) {
// re-authenticate
}
else {
// report and degrade gracefully
}
} Prevention
- Always include a valid Authorization header in options.headers.
- Verify options.baseURL resolves to a deployed transcription route before enabling Hearing.
- Consume result.fullStream or await result.text with try/catch; the async pump rejects both.
When it happens
Trigger: The fetcher (options.fetch or globalThis.fetch) POSTs audioStream to options.baseURL with options.headers and gets a non-2xx. Typical causes: 401/403 (missing or invalid Authorization header), 404 (wrong baseURL / transcription route not deployed), 413 (audio body too large), 415 (missing or wrong Content-Type header), 500/502/503 (upstream STT provider down).
Common situations: baseURL points at the wrong server or a path that is not a transcription endpoint; the auth token is absent or expired so headers lack a valid Bearer; the upstream STT provider (e.g. the configured Hearing backend) is temporarily unavailable; sending raw PCM without the Content-Type the server expects.
Related errors
- OpenRouter audio request failed: ${response.status} ${await
- Auth request failed (${response.status})
- Streaming transcription response is missing a readable body.
- Failed to fetch characters
- Failed to fetch character
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/2bd0fa812d52d72f.
Report an issue: GitHub.