pydantic/monty · error · Error
${what} produced no turn-ending event (worker crashed)
Error message
${what} produced no turn-ending event (worker crashed) What it means
`control()` sends a control request (create, dump, finish, etc.) to the wasm worker and treats 'no turn-ending event' as a hard failure: `run()` returned null, which only happens when the worker exited or crashed without producing an event. The error names the operation (`what`) so you can tell which control step lost its worker.
Source
Thrown at crates/monty-js/ts/worker/transport.ts:313
const aborted = await this.run({ tag: 'abort-feed', val: { excType: 'RuntimeError', message } }, onPrint)
turn = aborted ? this.toTurn(aborted) : crashed('worker exited without a turn-ending event')
// the component answers an abort with an error, never a suspension;
// servicing one would let a compromised worker call the host past
// the budget, so it ends the worker instead
if (turn.kind !== 'error' && turn.kind !== 'crashed') {
this.dead = true
turn = { kind: 'protocol', message: `worker answered abort-feed with ${turn.kind}` }
}
}
}
if (turn.kind === 'crashed') this.dead = true
return turn
}
/** Sends a control request and verifies its expected event kind. */
private async control(request: ComponentRequest, kind: ComponentEvent['tag'], what: string): Promise<ComponentEvent> {
const event = await this.run(request, undefined)
if (!event) throw new Error(`${what} produced no turn-ending event (worker crashed)`)
if (event.tag !== kind) throw new Error(`${what} expected event ${kind}, got ${event.tag}`)
return event
}
/** Runs one turn, forwarding buffered prints and retaining its terminator. */
private async run(request: ComponentRequest, onPrint: OnPrint | undefined): Promise<ComponentEvent | null> {
let events: ComponentEvent[]
try {
const result = await this.dispatcher(request)
if (result.status === 'shutdown') this.dead = true
// the component reports the limit in force (the configured one, else
// the 1000 default; a dump's on load), so it is adopted from the reply
if (request.tag === 'configure' || request.tag === 'load') {
this.suspensionLimit = result.maxSuspensions
this.suspensionsSeen = 0n
}
events = result.events
} catch {View on GitHub (pinned to adc986b362)
Solutions
- Discard the session and create a fresh one (pool.checkout()); the crashed worker cannot answer further requests.
- Inspect what the session was executing for OOM/timeout triggers (huge allocations, runaway loops) — hard limits intentionally end the worker.
- Check browser console / Node stderr for the underlying wasm trap message to distinguish memory traps from component bugs.
- If crashes are reproducible with trivial code, rebuild the component (`make build-wasm`) and re-test to rule out a stale/incorrect build.
Example fix
// before
const bytes = await session.dump() // worker crashed -> throws
// after
try {
const bytes = await session.dump()
} catch (err) {
// worker crashed; replace the session
session = await pool.checkout()
await session.feedRun(setupCode)
} Defensive patterns
Strategy: retry
Try / catch
try {
result = await session.dump()
} catch (err) {
if (err instanceof Error && err.message.includes('produced no turn-ending event (worker crashed)')) {
session = await pool.checkout() // replace crashed worker's session
result = await session.dump()
} else throw err
} Prevention
- Bound sandbox memory/loops so hard-limit traps are rare.
- Listen for worker error/exit events to detect crashes early.
- Keep one retry-and-replace path around all turn-level calls.
- Inspect wasm trap logs to distinguish OOM from component bugs.
When it happens
Trigger: The worker process/component died between sending the control request and receiving its event — e.g. wasm trap (OOM hard limit), browser `Worker.terminate()`, or the in-process degrade throwing during execution — so `run()` resolves null.
Common situations: Python code in the session triggering a hard memory-limit trap; the host page terminating workers (navigation, watchdog); a component bug that exits the worker mid-request; resource exhaustion killing the worker before it can answer.
Related errors
- Monty.create could not auto-load the monty wasm module in th
- the wasm worker does not support filesystem mounts (browser
- Dump returned an unexpected event
- ${what} expected event ${kind}, got ${event.tag}
- Monty wasm component requested process exit
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/53f149181aed7ebf.
Report an issue: GitHub.