pydantic/monty · error

checked Ready above

Error message

checked Ready above

What it means

In `handle_repl_feed` (crates/monty-proto/src/worker.rs:522) the worker asserts the session state is `Ready` via `unreachable!("checked Ready above")` after having already matched on the state earlier in the same request. The `mem::replace` pattern swaps the state out to `Configured(None)` and pattern-matches; if the state is anything but `Ready` here the worker panics. This is a child-side internal invariant: the earlier check and this extraction are expected to agree.

Source

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

    fn handle_repl_feed(&mut self, feed: pb::Feed, sink: &mut dyn EventSink) -> pb::ChildEvent {
        if let Err(event) = self.ensure_repl() {
            return *event;
        }
        if !matches!(self.state, SessionState::Ready(_)) {
            // ensure_repl left it un-Ready only when mid-suspension
            return protocol_violation("Feed without a session ready for input");
        }
        if !feed.skip_type_check
            && let Some(event) = self.type_check_feed(&feed.code)
        {
            return event;
        }
        let inputs = match named_inputs(feed.inputs) {
            Ok(inputs) => inputs,
            Err(event) => return *event,
        };
        let SessionState::Ready(mut repl) = mem::replace(&mut self.state, SessionState::Configured(None)) else {
            unreachable!("checked Ready above");
        };
        // The working directory persists in the REPL (including `os.chdir`);
        // the parent sends one only to switch it, and an older parent never does.
        if !feed.cwd.is_empty() {
            repl.set_cwd(&feed.cwd);
        }
        // snippets fed with skip_type_check never become type-check context:
        // the caller explicitly excluded them from checking, so later snippets
        // must not be checked against their (unchecked) bindings either
        if !feed.skip_type_check
            && let Some(state) = &mut self.type_check
        {
            state.pending_snippet = Some(feed.code.clone());
        }
        let mut print = ProtoPrint::new(sink, self.print_flush_interval);
        let result = repl.feed_start(&feed.code, inputs, PrintWriter::Callback(&mut print));
        let event = self.drive(result, &mut print);
        print.drain();

View on GitHub (pinned to adc986b362)

Solutions

  1. Read the surrounding `handle_repl_feed` code and restore the invariant: an `is Ready` check must immediately precede the `mem::replace` extraction with no intervening state mutation.
  2. Replace the fragile double-check with a single extraction that returns `protocol_violation(...)` instead of panicking on mismatch.
  3. Run the monty-proto worker tests (`cargo test -p monty-proto`) to confirm the REPL feed/resume alternation still holds.
  4. If seen in a deployed worker, upgrade/replace the worker: a panic here crashes the subprocess and the pool discards it.

Example fix

// before
let SessionState::Ready(mut repl) = mem::replace(&mut self.state, SessionState::Configured(None)) else {
    unreachable!("checked Ready above");
};
// after
let SessionState::Ready(mut repl) = mem::replace(&mut self.state, SessionState::Configured(None)) else {
    return protocol_violation("feed requires a Ready session");
};
Defensive patterns

Strategy: validation

Validate before calling

// Parent: only feed while the session is ready
if (sessionState !== 'ready') throw new Error('feed requires a ready session');

Type guard

fn is_ready(s: &SessionState) -> bool { matches!(s, SessionState::Ready(_)) }

Try / catch

match worker_result { Ok(ev) => .., Err(e) if e.is_crash() => pool.replace_worker(), }

Prevention

When it happens

Trigger: A `ReplFeed` request handled by `handle` → `handle_repl_feed` where the state was `Ready` at the first check but the second `let SessionState::Ready(...) = mem::replace(...)` fails — only possible if the state machine was mutated between the two checks or the earlier check was removed/changed during refactoring.

Common situations: Protocol state-machine refactors in monty-proto; adding a new request kind that mutates `self.state` without updating the check order; a compromised/buggy child implementation diverging from the documented alternation contract.

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