moeru-ai/airi · error · Error

audio models upstream ${res.status}: ${await res.text().catc

Error message

audio models upstream ${res.status}: ${await res.text().catch(() => '')}

What it means

Thrown by the official speech provider's `listModels()` when the AIRI server endpoint `${SERVER_URL}/api/v1/audio/models` (proxied to the unspeech gateway) replies with a non-2xx status. The request carries `Authorization: Bearer <token>` from `getAuthToken()`, so auth failures and server-side upstream configuration failures are the two dominant causes. The body is truncated to 256 chars in the message.

Source

Thrown at packages/stage-ui/src/libs/providers/providers/official/index.ts:132

    const provider = createOfficialAudioProvider()
    const originalSpeech = provider.speech.bind(provider)
    provider.speech = (model: string, extraOptions?: Record<string, unknown>) => {
      const result = {
        ...originalSpeech(model),
        ...extraOptions,
      }
      result.fetch = withCredentials()
      return result
    }
    return provider
  },
  validationRequiredWhen: () => false,
  extraMethods: {
    listModels: async (): Promise<ModelInfo[]> => {
      defaultSpeechModelId = null
      const res = await globalThis.fetch(`${SERVER_URL}/api/v1/audio/models`, { headers: authHeaders() })
      if (!res.ok)
        throw new Error(`audio models upstream ${res.status}: ${await res.text().catch(() => '')}`.slice(0, 256))

      const data = await res.json() as { models?: { id: string, name: string, description?: string }[], default?: string | null }
      if (!Array.isArray(data.models))
        throw new Error('audio models upstream returned malformed body')

      defaultSpeechModelId = typeof data.default === 'string' && data.default.length > 0 ? data.default : null

      return data.models.map(m => ({
        id: m.id,
        name: m.name,
        description: m.description,
        provider: OFFICIAL_SPEECH_PROVIDER_ID,
      }))
    },
    listVoices: async (_config, _provider, model): Promise<VoiceInfo[]> => {
      // Voice catalogs are model-scoped on the server side. Pass the active
      // model through so Azure / cosyvoice / future provider voices route to
      // the right adapter. If model discovery has not completed yet, keep the

View on GitHub (pinned to 677329427f)

Solutions

  1. Re-authenticate (sign out/in) so `getAuthToken()` returns a fresh JWT, then retry the model listing.
  2. Confirm the client's `SERVER_URL` points at a running server that serves `/api/v1/audio/models` (curl it with the bearer token).
  3. Inspect the server logs for the unspeech upstream error behind a 502/503 and fix `UNSPEECH_UPSTREAM` configuration.
  4. If the server is old, update it to a revision shipping the audio models route.

Example fix

// before
const models = await provider.extraMethods.listModels()
// after — only probe when authenticated, and report the upstream cause
import { getAuthToken } from '../libs/auth'
const token = getAuthToken()
if (!token)
  throw new Error('Sign in before listing official speech models')
const models = await provider.extraMethods.listModels()
Defensive patterns

Strategy: try-catch

Validate before calling

import { getAuthToken } from '../libs/auth'
const token = getAuthToken()
if (!token)
  throw new Error('Authentication required to list official speech models')

Try / catch

try {
  const models = await provider.extraMethods.listModels()
}
catch (error) {
  const message = errorMessageFrom(error)
  if (message.includes('401'))
    // re-authenticate then retry
  else
    // treat as server/upstream misconfiguration; surface upstream body (truncated to 256 chars)
}

Prevention

When it happens

Trigger: Calling `listModels()` while signed out or with an expired JWT (401), pointing `SERVER_URL` at a server that lacks the audio route (404), or the server's `UNSPEECH_UPSTREAM` being unreachable/misconfigured so the gateway returns 502/503.

Common situations: Session expired overnight and the speech settings page probes models on mount; local docker-compose backend not running while the client still points at it; server deployed from an older revision without the unspeech routes; auth service (server/apps/auth) token audience mismatch after redeploying only one service.

Related errors


AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18). Data as JSON: /api/errors/b1d721f81497339f. Report an issue: GitHub.