moeru-ai/airi · warning
[Kokoro Worker] Unknown message type:
Error message
[Kokoro Worker] Unknown message type:
What it means
The Kokoro TTS worker's inbound message switch handles exactly 'load-model', 'run-inference', 'unload-model', and 'cancel'. Any other message.type falls into default, logs this warning with the received type, and is then dropped - no reply and no error is sent, so a caller awaiting that requestId would hang until its timeout.
Source
Thrown at packages/stage-ui/src/workers/kokoro/worker.ts:302
switch (message.type) {
case 'load-model':
await loadModel(message)
break
case 'run-inference':
await runInference(message as RunInferenceRequest<KokoroInferenceInput>)
break
case 'unload-model':
ttsModel = null
currentQuantization = null
currentDevice = null
globalThis.postMessage({ type: 'model-unloaded', requestId: message.requestId })
break
case 'cancel':
markCancelled(message.targetRequestId)
break
default:
console.warn('[Kokoro Worker] Unknown message type:', (message as any).type)
}
})
View on GitHub (pinned to 677329427f)
Solutions
- Compare the logged type against the WorkerInboundMessage union in the worker protocol module
- Hard-reload or restart the app so main thread and worker come from the same bundle
- Add the missing case in the worker switch (or reply with an error) if you extended the protocol
- Use the typed request helpers instead of hand-built postMessage payloads
Example fix
// before
switch (message.type) {
case 'load-model': ...
default: console.warn('[Kokoro Worker] Unknown message type:', (message as any).type)
}
// after - compile-time exhaustiveness plus an error reply so callers do not hang
default: {
const _exhaustive: never = message
sendError(message.requestId, new Error(`Unknown message type: ${(message as any).type}`), 'protocol')
} Defensive patterns
Strategy: type-guard
Validate before calling
if (!isKnownKokoroMessage(message)) {
console.warn('dropping unknown worker message:', message)
return
} Type guard
type KnownKokoroMessage =
| { type: 'load-model' }
| { type: 'run-inference' }
| { type: 'unload-model' }
| { type: 'cancel' }
function isKnownKokoroMessage(m: unknown): m is KnownKokoroMessage {
return typeof m === 'object' && m !== null
&& ['load-model', 'run-inference', 'unload-model', 'cancel'].includes((m as { type: unknown }).type as string)
} Try / catch
default: {
const _exhaustive: never = message
sendError(message.requestId, new Error(`Unknown message type: ${(message as any).type}`), 'protocol')
} Prevention
- Define the message union in one shared protocol module imported by both sides
- Use exhaustive never checks in the switch so new types fail compilation
- Reply with an error for unknown requests so awaiting callers time out with a reason
- Hard-reload after deployments to avoid main/worker version skew
When it happens
Trigger: Main thread and worker built from different versions after a deploy (new message type exists on one side only); raw postMessage with a typo'd or renamed type; dev HMR leaving a stale worker alive that receives new-protocol messages; external code posting into the worker.
Common situations: Version skew between bundles after upgrades without a hard reload; developing a new worker message and forgetting the worker-side case; recorded/replayed messages with outdated shapes.
Related errors
- Web worker transport is not available in node runtime.
- Web worker transport is not implemented yet.
- Kokoro TTS generation failed: No model loaded.
- [WhisperAdapter] Restarting in ${delay}ms (attempt ${restart
- [InferenceWorkerManager] Restarting worker in ${delay}ms (at
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/e149c3991f90576f.
Report an issue: GitHub.