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
- Ensure the monty worker binary version matches the @pydantic/monty package version
- Retry the turn on a fresh session; the pool detects crashes and replaces workers
- 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
- Keep the monty worker binary version in lockstep with the JS package version
- Never reuse future/call ids across sessions; let the library manage them
- Treat ProtocolError as fatal to the session — do not retry on the same session
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
- worker reported ResolveFutures with no pending call ids
- Dump returned an unexpected event
- ClassInstance expects an object instance
- notCallableMessage(method)
- ClassInstance expects an instance of a class, not a null-pro
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/ff9d097ead6c0f80.
Report an issue: GitHub.