moeru-ai/airi · error · Error
Kokoro TTS generation failed: No model loaded.
Error message
Kokoro TTS generation failed: No model loaded.
What it means
The Kokoro TTS web worker keeps ttsModel null until its async init (weights download plus WASM/ONNX setup) completes; a 'generate' action arriving before that hits this guard instead of crashing on ttsModel.generate. Note that getVoices answers from the static voice list, so a successful voices response does not imply the model is loaded.
Source
Thrown at packages/stage-ui/src/workers/kokoro/worker.ts:251
throw new Error('Model not loaded. Send load-model first.')
if (isCancelled(requestId)) {
clearCancelled(requestId)
return
}
const result: InferenceResultResponse<KokoroVoicesOutput> = {
type: 'inference-result',
requestId,
output: { action: 'getVoices', voices: ttsModel.voices },
}
globalThis.postMessage(result)
return
}
// action === 'generate'
if (!ttsModel)
throw new Error('Kokoro TTS generation failed: No model loaded.')
const { text, voice } = input
const audioResult = await ttsModel.generate(text, { voice })
if (isCancelled(requestId)) {
clearCancelled(requestId)
return
}
// Transfer raw PCM Float32Array directly — avoids WAV blob encode/decode overhead.
const samples = audioResult.audio
const result: InferenceResultResponse<KokoroGenerateOutput> = {
type: 'inference-result',
requestId,
output: { action: 'generate', samples, samplingRate: audioResult.sampling_rate },
}
;(globalThis as any).postMessage(result, [samples.buffer])
}View on GitHub (pinned to 677329427f)
Solutions
- Await the worker's init/ready response before enabling generation.
- Treat any init error response as fatal for the session and re-init before the next generate.
- Queue generate requests until ready instead of posting them eagerly.
- If the weights download failed, clear the model cache and re-init.
Example fix
// before
worker.postMessage({ type: 'inference', requestId, input: { action: 'generate', text, voice } })
// after
await kokoroReady // promise resolved by the init-result message
worker.postMessage({ type: 'inference', requestId, input: { action: 'generate', text, voice } }) Defensive patterns
Strategy: validation
Validate before calling
let ready = false
worker.addEventListener('message', (e) => {
if (e.data?.type === 'inference-result' && e.data.output?.action === 'init')
ready = true
})
if (!ready)
await initPromise Try / catch
try {
await generate(text, voice)
}
catch (e) {
if (errorMessageFrom(e)?.includes('No model loaded')) {
await initWorker()
return await generate(text, voice) // single retry after re-init
}
throw e
} Prevention
- Disable speak buttons until the init-result message arrives.
- Track worker lifecycle: on replace or reload, reset the ready state.
- Surface init errors in the same UI that shows generate errors.
When it happens
Trigger: UI posts an inference 'generate' immediately after spawning the worker; an earlier init failed (its error response was missed) and a later generate is sent; the worker was restarted by page reload or HMR while UI state still says ready.
Common situations: First-use race on slow networks; HMR during development resetting worker state; init error handling not wired so failures stay silent until generate.
Related errors
- [KokoroAdapter] Restarting in ${delay}ms (attempt ${restartA
- Unknown Kokoro voice: ${body.voice}
- [Kokoro Worker] Failed with dtype=${attempt.dtype} device=${
- Failed to initialize voice activity detection.
- Web worker transport is not available in node runtime.
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/e661503516d13e1f.
Report an issue: GitHub.