pydantic/monty · error · ProtocolError

worker reported unknown pending call id ${id}

Error message

worker reported unknown pending call id ${id}

What it means

The worker sent a `ResolveFutures` turn listing a pending external-call id the host has no record of. This breaks the protocol contract (both sides must agree on outstanding future ids), so a `ProtocolError` is thrown — it indicates a worker/host desync or a bug, not user input.

Source

Thrown at crates/monty-js/ts/session.ts:711

        future.outcome = { ok }
      },
      (err: unknown) => {
        future.done = true
        future.outcome = { err }
      },
    )
    this.futures.set(callId, future)
  }

  /**
   * Every sandbox task is blocked: wait until at least one pending future
   * settles, then deliver everything that is ready.
   */
  private async answerResolveFutures(event: ResolveFuturesTurn, onPrint: PrintCallback): Promise<object> {
    const pending = event.pendingCallIds.map((id) => {
      const future = this.futures.get(id)
      if (future === undefined) {
        throw new ProtocolError(`worker reported unknown pending call id ${id}`)
      }
      return future
    })
    if (pending.length === 0) {
      throw new ProtocolError('worker reported ResolveFutures with no pending call ids')
    }
    await Promise.race(pending.map((f) => f.settled))
    const results: NativeFutureResult[] = pending
      .filter((f) => f.done)
      .map((f) => {
        this.futures.delete(f.callId)
        const outcome = f.outcome!
        if ('ok' in outcome) {
          // a prepare rejection is delivered as the future's error, matching
          // how a sync return value's conversion failure surfaces
          try {
            return { callId: f.callId, ok: true, value: prepare(outcome.ok, this.instances) }
          } catch (err) {

View on GitHub (pinned to adc986b362)

Solutions

  1. Ensure the monty worker binary version matches the @pydantic/monty package version
  2. Retry the turn on a fresh session; the pool detects crashes and replaces workers
  3. If reproducible, file a bug with the turn/event trace — this is an internal invariant violation
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await session.feedRun(code, { externalLookup })
} catch (e) {
  if (e instanceof ProtocolError && e.message.includes('unknown pending call id')) {
    // worker desync: discard session, let the pool replace the worker
    session = await pool.checkout()
  } else throw e
}

Prevention

When it happens

Trigger: A `ResolveFutures` event arrives whose `pendingCallIds` contains an id never registered by a host-side external call, or an id already resolved and deleted from the host map; typically only reachable via a mismatched/buggy worker binary or protocol version skew.

Common situations: Version mismatch between the JS package and the monty worker binary, a compromised or buggy worker, replaying turns out of order.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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