pydantic/monty · error

checked above

Error message

checked above

What it means

In `handle_resume_call` (crates/monty-proto/src/worker.rs:573) the `NotHandled` branch asserts the session is `Suspended` with `unreachable!("checked above")`. An earlier guard on `self.state` should have established `SessionState::Suspended`; if the state is not suspended when a `NotHandled` result arrives, the worker panics rather than returning a protocol violation.

Source

Thrown at crates/monty-proto/src/worker.rs:573

        let Some(call_id) = expected_call_id else {
            return protocol_violation("ResumeCall without a suspended function/OS call");
        };
        if resume.call_id != call_id {
            return protocol_violation(&format!(
                "ResumeCall call_id {} does not match {call_id}",
                resume.call_id
            ));
        }
        let Some(wire_result) = resume.result else {
            return protocol_violation("ResumeCall has no result");
        };
        // NotHandled resolves against the suspended call itself — the child
        // owns the no-handler semantics (`OsFunctionCall::on_no_handler`), so
        // the parent never has to compute or echo the default exception.
        let result: ExtFunctionResult =
            if matches!(wire_result.kind, Some(pb::ext_function_result::Kind::NotHandled(_))) {
                let SessionState::Suspended(progress) = &self.state else {
                    unreachable!("checked above");
                };
                let ReplProgress::OsCall(call) = progress.as_ref() else {
                    return protocol_violation("NotHandled is only valid answering a suspended OS call");
                };
                ExtFunctionResult::Error(call.function_call.on_no_handler())
            } else {
                match wire_result.try_into() {
                    Ok(result) => result,
                    Err(err) => return protocol_violation(&format!("invalid result: {err}")),
                }
            };
        let SessionState::Suspended(progress) = mem::replace(&mut self.state, SessionState::Configured(None)) else {
            unreachable!("checked above");
        };
        let mut print = ProtoPrint::new(sink, self.print_flush_interval);
        let outcome = match *progress {
            ReplProgress::FunctionCall(call) => call.resume(result, PrintWriter::Callback(&mut print)),
            ReplProgress::OsCall(call) => call.resume(result, PrintWriter::Callback(&mut print)),

View on GitHub (pinned to adc986b362)

Solutions

  1. Ensure the parent sends at most one resume per suspension and only while the child reported a suspended progress event.
  2. In worker.rs, collapse the check: perform the `Suspended` extraction once and return `protocol_violation("resume requires a suspended session")` on mismatch instead of panicking.
  3. Check git history around line 573 for a refactor that separated the guard from the unreachable and reunify them.
  4. Replace the worker if this fires in production — the pool will detect the crashed child and respawn it.

Example fix

// before
let SessionState::Suspended(progress) = &self.state else { unreachable!("checked above") };
// after
let SessionState::Suspended(progress) = &self.state else {
    return protocol_violation("NotHandled requires a suspended session");
};
Defensive patterns

Strategy: validation

Validate before calling

// Parent: answer each suspension at most once
if (!suspended || alreadyResumed) throw new Error('no pending suspension to answer with NotHandled');

Type guard

const isSuspended = (s: SessionState): s is { kind: 'Suspended' } => s.kind === 'Suspended';

Try / catch

try { await session.resume({ notHandled: true }) } catch (e) { if (e instanceof MontyCrashedError) pool.replace(); }

Prevention

When it happens

Trigger: A `ResumeCall` request with `kind = NotHandled` arriving while `self.state` is not `SessionState::Suspended` — e.g. resuming a session that was never suspended, or whose suspension was already consumed by a previous resume.

Common situations: A buggy parent sending two resumes for one suspension; protocol refactors that moved or removed the earlier `Suspended` check; replaying a request against a session in the wrong phase.

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/c78a8a222c09529a. Report an issue: GitHub.