moeru-ai/airi · error

Streaming model id missing backend prefix: ${requestedModel}

Error message

Streaming model id missing backend prefix: ${requestedModel}

What it means

Format guard in the official streaming speech playground: streaming model ids must be shaped `<backend>/<resource>` (e.g. `volcengine/seed-tts-2.0`) because the trailing path segment is forwarded to the upstream as Volcengine's `api_resource_id`. The code computes `slashIndex = requestedModel.indexOf('/')` and refuses ids without a slash, deliberately, so a bare resource id never silently degrades to a wrong/default upstream resource — the comment in source calls this out as hiding config drift.

Source

Thrown at packages/stage-pages/src/pages/settings/providers/speech/official-provider-speech-streaming.vue:103

// Synthesize via the streaming session helper. The page uses the SAME
// transport the runtime pipeline uses (ws → API proxy → unspeech
// bridge → Volcengine v3 bidirectional) so the preview faithfully
// represents what the user hears in actual chat. The session is opened
// per-preview because there's no LLM token stream here — we just send
// one `text` frame containing the static preview prompt.
async function handleGenerateSpeech(input: string, voiceId: string, _useSSML: boolean): Promise<ArrayBuffer> {
  const requestedModel = model.value
  if (!requestedModel)
    throw new Error('No streaming TTS model selected and server returned no default')
  // `model` looks like `volcengine/seed-tts-2.0`. The trailing path is
  // forwarded as Volcengine's `api_resource_id` so the upstream knows which
  // model variant to use; matches the wiring in `Stage.vue`. We require the
  // `<backend>/<resource>` shape and refuse anything else — silently picking
  // a fallback resource id hides config drift.
  const slashIndex = requestedModel.indexOf('/')
  if (slashIndex < 0)
    throw new Error(`Streaming model id missing backend prefix: ${requestedModel}`)
  const apiResourceId = requestedModel.slice(slashIndex + 1)
  const result = await streamingSynthesize({
    model: requestedModel,
    voice: voiceId,
    input,
    extraBody: {
      api_resource_id: apiResourceId,
      audio: { sample_rate: 24000, bit_rate: 64000 },
    },
  })
  return result.audio
}

function handleLogin() {
  needsLogin.value = true
}
</script>

View on GitHub (pinned to 677329427f)

Solutions

  1. Pick the model from the server-populated dropdown instead of typing one — catalog entries carry the `<backend>/<resource>` shape.
  2. If the stored value is stale, edit the provider config so `model` includes the backend prefix (e.g. `volcengine/seed-tts-2.0`).
  3. If catalog entries themselves arrive without a prefix, report the server bug — the client intentionally refuses to guess.
  4. Add a migration/default when reading stored config so legacy bare ids are upgraded rather than crashing at generate time.

Example fix

// before
const slashIndex = requestedModel.indexOf('/')
if (slashIndex < 0)
  throw new Error(`Streaming model id missing backend prefix: ${requestedModel}`)

// after
if (!/^[a-z0-9-]+\/.+$/i.test(requestedModel))
  throw new Error(`Streaming model id must look like '<backend>/<resource>', got: ${requestedModel}`)
Defensive patterns

Strategy: type-guard

Validate before calling

const STREAMING_MODEL_ID_PATTERN = /^[a-z0-9-]+\/.+$/i
const modelIdValid = computed(() => STREAMING_MODEL_ID_PATTERN.test(model.value ?? ''))
// template: <button :disabled="!modelIdValid">Generate</button>

Type guard

function isStreamingModelId(id: string): boolean {
  // '<backend>/<resource>' — backend segment non-empty, resource segment non-empty
  return /^[^/\s]+\/.+[^/\s]$/.test(id) && !id.startsWith('/') && !id.endsWith('/')
}

Try / catch

try { const apiResourceId = requestedModel.slice(requestedModel.indexOf('/') + 1) }
catch { /* validation happens before slicing; keep the explicit error message with the offending id */ }

Prevention

When it happens

Trigger: A stored provider config `model` value like `seed-tts-2.0` (no backend prefix) — from manual config edits, older formats, or a server catalog entry missing its prefix; user typing a custom model id directly into the picker; a backend rename that dropped the prefix from returned catalog entries.

Common situations: Migrating stored settings from an older version that stored bare resource ids; hand-editing provider config files; a server catalog regression listing resource-only ids; copy-pasting model names from Volcengine docs without the backend namespace.

Related errors


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