moeru-ai/airi · error · Error
Speech engine answered ${response.status} ${response.statusT
Error message
Speech engine answered ${response.status} ${response.statusText} for /${engineRequest.endpoint}${suffix} What it means
request() treats any non-ok HTTP response as fatal and throws with the status, statusText, endpoint, and up to 200 chars of the response body as detail. This is the generic engine-side error surface: the request reached a VOICEVOX-compatible engine but the engine rejected it (or an intermediary rejected it).
Source
Thrown at packages/stage-ui/src/libs/providers/providers/voicevox/engine.ts:207
throw new Error('The Base URL is not an absolute http:// or https:// address.')
}
const doFetch = options?.fetch ?? globalThis.fetch
const response = await doFetch(url, {
method: POST_ENDPOINTS.has(engineRequest.endpoint) ? 'POST' : 'GET',
// No engine in the family redirects. A redirect therefore means the Base URL
// points at something else, and following it would hide that.
redirect: 'error',
signal: options?.signal,
...(engineRequest.body === undefined
? {}
: { body: JSON.stringify(engineRequest.body), headers: { 'Content-Type': 'application/json' } }),
})
if (!response.ok) {
const detail = (await response.text()).trim()
const suffix = detail ? `: ${detail.slice(0, 200)}` : ''
throw new Error(`Speech engine answered ${response.status} ${response.statusText} for /${engineRequest.endpoint}${suffix}`)
}
return response
}
View on GitHub (pinned to 9c213115f8)
Solutions
- Read the `: <detail>` suffix in the message — it contains the engine's own error text identifying the root cause.
- Re-fetch the speaker list from the engine (/speakers) and re-select the voice so ids match the running engine.
- Confirm the Base URL points at the intended engine and version; restart the engine if it returns 500/503.
- If using a proxy, ensure it forwards POST bodies with Content-Type application/json untouched.
Defensive patterns
Strategy: try-catch
Try / catch
try {
await synthesize(request)
}
catch (error) {
const message = errorMessageFrom(error)
const match = message.match(/answered (\d{3})/)
if (match?.[1] === '404')
await refreshSpeakersAndReselectVoice()
else
showSpeechError(message)
} Prevention
- Parse the `: <detail>` suffix for the engine's own error explanation.
- Refresh the speaker list whenever the engine version or instance changes.
- Add retry with backoff only for 5xx statuses, not 4xx.
- Monitor engine health (GET /version) before synthesis sessions.
When it happens
Trigger: Any endpoint returning 4xx/5xx: /audio_query with an unknown speaker id (400/404/422), engine overloaded (500/503), unsupported endpoint on a compatible engine like CoqPIt/ShareVOX (404), auth/proxy rejection (401/403).
Common situations: Voice id from a different engine version (speaker id no longer exists); engine still initializing; style id out of range; Base URL behind a proxy that strips or blocks POST; wrong engine family exposing different endpoint paths.
Related errors
- Speech engine answered /${endpoint} with a body that is not
- Gemini TTS request failed: ${response.status} ${await respon
- MiMo TTS request failed: ${response.status} ${response.statu
- MiMo transcription failed: ${response.status} ${response.sta
- MiniMax TTS request failed: ${response.status} ${response.st
AI-assisted analysis of moeru-ai/airi@9c213115f8 (2026-09-02).
Data as JSON: /api/errors/a191c4e278822400.
Report an issue: GitHub.