moeru-ai/airi · error · Error

Failed to fetch voices: ${response.statusText}

Error message

Failed to fetch voices: ${response.statusText}

What it means

Thrown by listVoices() in the IndexTTS vLLM provider when the GET to the voices endpoint returns non-2xx. The voices endpoint is `${baseUrl}audio/voices`; a non-ok response means the IndexTTS vLLM server is unreachable or the base URL is wrong. Only the HTTP statusText is included (no body parsing).

Source

Thrown at packages/stage-ui/src/libs/providers/providers/index-tts-vllm/index.ts:81

          return { errors: [], reason: '', reasonKey: '', valid: true }
        },
      }),
    ],
  },
  extraMethods: {
    listModels: async () => [{
      id: 'IndexTTS-1.5',
      name: 'IndexTTS-1.5',
      provider: 'index-tts-vllm',
      description: 'Default model for Index-TTS vLLM deployment',
      contextLength: 0,
      deprecated: false,
    }],
    listVoices: async (config) => {
      const response = await fetch(voicesUrl(config))
      if (!response.ok)
        throw new Error(`Failed to fetch voices: ${response.statusText}`)

      const voices = await response.json() as Record<string, unknown>
      return Object.keys(voices).map(voice => ({
        id: voice,
        name: voice,
        provider: 'index-tts-vllm',
        languages: [{ code: 'cn', title: 'Chinese' }, { code: 'en', title: 'English' }],
      }))
    },
  },
})

View on GitHub (pinned to 27111382b4)

Solutions

  1. Confirm the IndexTTS vLLM server is running and healthy at the configured baseUrl.
  2. Verify baseUrl is correct (default http://localhost:11996/tts/); adjust port/host to match the deployment.
  3. curl ${baseUrl}audio/voices directly to confirm the route exists and returns 200 before configuring the provider.
  4. If the deployed server lacks /audio/voices, upgrade the IndexTTS vLLM image or pick a version that exposes it.
Defensive patterns

Strategy: try-catch

Validate before calling

async function isIndexTtsVoicesReachable(baseUrl: string, signal?: AbortSignal): Promise<boolean> {
  try {
    const res = await fetch(`${baseUrl}audio/voices`, { signal })
    return res.ok
  }
  catch {
    return false
  }
}
// before configuring the provider / calling listVoices:
if (!await isIndexTtsVoicesReachable(config.baseUrl)) {
  // IndexTTS vLLM not reachable; do not call listVoices
}

Type guard

null

Try / catch

try {
  const voices = await providerIndexTtsVllm.extraMethods!.listVoices!(config)
}
catch (err) {
  if (err instanceof Error && err.message.startsWith('Failed to fetch voices')) {
    // check IndexTTS server health and baseUrl; the validator runs the same check at config time
  }
  else throw err
}

Prevention

When it happens

Trigger: fetch(voicesUrl(config)) returns non-ok. The IndexTTS vLLM server is not running, the baseUrl points at the wrong host/port/path, the /audio/voices route is absent in the deployed version, or the server returned an error status.

Common situations: IndexTTS vLLM container not started. baseUrl left at default http://localhost:11996/tts/ but the server runs elsewhere. Server version without the /audio/voices route. Port mismatch / firewall blocking localhost. CORS in browser.

Related errors


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