moeru-ai/airi · error

OpenRouter audio request failed: ${response.status} ${await

Error message

OpenRouter audio request failed: ${response.status} ${await response.text()}

What it means

The OpenRouter adapter posts to OpenRouter's chat/completions with modalities ['text','audio'] and streaming enabled. If OpenRouter returns a non-ok HTTP response, the status and the raw response text (which holds OpenRouter's error JSON) are thrown as this error. It is an upstream API failure.

Source

Thrown at packages/provider-inference/src/providers/cloud/openrouter-audio-speech/index.ts:114

    const body = JSON.parse(init.body) as { input?: string, voice?: string }
    const response = await globalThis.fetch(new URL('chat/completions', baseUrl), {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
        ...OPENROUTER_ATTRIBUTION_HEADERS,
      },
      body: JSON.stringify({
        model,
        messages: [{ role: 'user', content: ttsPrompt(body.input ?? '') }],
        modalities: ['text', 'audio'],
        audio: { voice: body.voice, format: 'pcm16' },
        stream: true,
      }),
    })
    if (!response.ok)
      throw new Error(`OpenRouter audio request failed: ${response.status} ${await response.text()}`)
    if (!response.body)
      throw new Error('OpenRouter audio response has no body')

    const wav = toWavFromPCM16(decodeBase64Pcm(await collectAudioChunks(response.body)), 24000)
    return new Response(new Blob([wav], { type: 'audio/wav' }), {
      status: 200,
      headers: { 'Content-Type': 'audio/wav' },
    })
  }
}

export const providerOpenRouterAudioSpeech = defineProvider<OpenRouterAudioConfig, 'openrouter-audio-speech'>({
  id: 'openrouter-audio-speech',
  name: 'OpenRouter',
  nameLocalize: ({ t }) => t('settings.pages.providers.provider.openrouter-audio-speech.title'),
  description: 'openrouter.ai',
  descriptionLocalize: ({ t }) => t('settings.pages.providers.provider.openrouter-audio-speech.description'),
  tasks: ['text-to-speech'],

View on GitHub (pinned to f679616c34)

Solutions

  1. Read the error text — it contains OpenRouter's error message (e.g. 'no audio capable model found').
  2. Choose a model that supports audio output (audio in modalities) on OpenRouter.
  3. Verify the OpenRouter API key and remaining credits.
  4. Check that the requested voice is supported by the routed model.
  5. Retry with backoff on 429/5xx.

Example fix

// before
const model = 'openai/gpt-4o-mini' // no audio output support
// after
const model = 'openai/gpt-4o-audio-preview' // audio-capable model
Defensive patterns

Strategy: try-catch

Validate before calling

if (!openRouterApiKey) throw new Error('OpenRouter API key required')
if (!audioCapableModel) throw new Error('Selected model must support audio output')

Try / catch

try { await speak(text) } catch (e) {
  if (/failed: 4\d\d/.test(e.message)) showUpstreamError(e.message) // includes OpenRouter error JSON
  if (/failed: (429|5\d\d)/.test(e.message)) await backoffRetry()
}

Prevention

When it happens

Trigger: OpenRouter returns 401 (invalid key/credits), 404 (model doesn't support audio output), 400 (invalid voice or modality combination), 402 (insufficient credits), 429 (rate limit), or 5xx.

Common situations: Selected model lacks audio output support; voice id not supported by the routed model; exhausted OpenRouter credits; wrong API key; model provider outage.

Related errors


AI-assisted analysis of moeru-ai/airi@f679616c34 (2026-09-08). Data as JSON: /api/errors/815613750606c4bb. Report an issue: GitHub.