pydantic/monty · error · Error

Dump returned an unexpected event

Error message

Dump returned an unexpected event

What it means

`session.dump()` sends a dump control request to the worker and expects exactly one `dump-result` event back. If the worker answers with a different event tag, the transport's internal protocol invariant is broken, so it throws instead of returning bogus bytes. This indicates a worker/protocol mismatch rather than bad user input.

Source

Thrown at crates/monty-js/ts/worker/transport.ts:244

    return this.turn(
      {
        tag: 'resume-futures',
        val: results.map((result) => ({
          callId: result.callId,
          outcome: result.ok
            ? { tag: 'return-value', val: encodeValue(result.value) }
            : errorResult(result.excType ?? 'RuntimeError', result.message ?? ''),
        })),
      },
      onPrint,
    )
  }

  /** Dumps the current session into opaque bytes. */
  async dump(): Promise<Uint8Array> {
    const event = await this.control({ tag: 'dump' }, 'dump-result', 'Dump')
    if (event.tag === 'dump-result') return event.val
    throw new Error('Dump returned an unexpected event')
  }

  /** Restores a previously dumped session into this fresh worker. */
  async restore(
    state: Uint8Array,
    mounts: readonly unknown[],
    onPrint: OnPrint,
  ): Promise<NativeTurn | { kind: 'loaded' }> {
    if (mounts.length > 0) {
      throw new Error('the wasm worker does not support filesystem mounts (browser has no host filesystem)')
    }
    const event = await this.run({ tag: 'load', val: state }, onPrint)
    if (!event) return crashed('worker exited without a turn-ending event')
    return event.tag === 'ok' ? { kind: 'loaded' } : this.enforceSuspensionLimit(this.toTurn(event), onPrint)
  }

  /** Resets a live worker for reuse and disposes a dead worker. */
  async finish(): Promise<void> {

View on GitHub (pinned to adc986b362)

Solutions

  1. Rebuild the wasm component and refresh the WIT-derived declarations together (`make build-wasm`) so the component and `ts/worker` agree on the protocol.
  2. Verify the session's worker is alive (no prior crash/terminate) before dumping; create a fresh session/pool if the worker crashed.
  3. Check that the installed `@pydantic/monty` JS version and the compiled component come from the same source tree / release.
  4. If reproducible, file a bug with the event tag observed — this path should be unreachable for compliant workers.

Example fix

// before
// component built from an older revision than ts/worker
const bytes = await session.dump() // unexpected event tag

// after
make build-wasm   # rebuild component + regenerate checked-in declarations
npm run build      # rebuild TS layer against them
const bytes = await session.dump()
Defensive patterns

Strategy: try-catch

Try / catch

try {
  bytes = await session.dump()
} catch (err) {
  if (err instanceof Error && err.message === 'Dump returned an unexpected event') {
    // protocol skew or dead worker: rebuild session / re-verify component build
    session = await pool.checkout()
  } else throw err
}

Prevention

When it happens

Trigger: The worker responds to a `{ tag: 'dump' }` control request with an event other than `dump-result` — e.g. a crashed/terminated worker surfaced a terminator event, or the wasm component and JS transport versions disagree on the event schema.

Common situations: Mixed-version deployments where the compiled wasm component is older/newer than the TypeScript layer; a worker that died mid-request so `run` returned a stale/terminator event; custom forks of the worker protocol changing event tags.

Related errors


AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13). Data as JSON: /api/errors/742c77900fbabe5c. Report an issue: GitHub.