pydantic/monty · critical

StoreCell: entry is not a Cell

Error message

StoreCell: entry is not a Cell

What it means

`store_cell` swaps a new value into the heap Cell behind a local slot; this panic fires if the heap entry at `cell_id` is not a `Cell`. Cell entries are allocated once per closed-over variable, so a type mismatch means heap corruption or an interpreter bug, not a Python-level failure.

Source

Thrown at crates/monty/src/bytecode/vm/mod.rs:2543

        let name_str = match name {
            Some(id) => self.interns.get_str(id).to_string(),
            None => "<free var>".to_string(),
        };
        ExcType::name_error_free_variable(&name_str).into()
    }

    /// Pops the top of stack and stores it in a closure cell.
    ///
    /// The cell `HeapId` is read from the frame's local variable slot on the stack.
    fn store_cell(&mut self, slot: u16) {
        let value = self.pop();
        // The guard will clean up the new value if we panic, or the old value if we swap
        let this = self;
        defer_drop_mut!(value, this);

        let cell_id = this.cell_id_from_local(slot);
        let HeapReadOutput::Cell(mut cell) = this.heap.read(cell_id) else {
            panic!("StoreCell: entry is not a Cell")
        };
        mem::swap(&mut cell.get_mut(this.heap).0, value);
    }

    /// Unbinds a closure cell: replaces its contents with `Undefined`, so a
    /// later [`Self::load_cell`] raises the free-variable `NameError` —
    /// CPython's `DELETE_DEREF` cleanup of a captured `except ... as` target.
    /// The only emitter stores `None` first, so the cell is never already
    /// unbound here (no error path, unlike [`Self::delete_global`]).
    fn delete_cell(&mut self, slot: u16) {
        let value = Value::Undefined;
        // the guard drops the cell's previous contents after the swap
        let this = self;
        defer_drop_mut!(value, this);

        let cell_id = this.cell_id_from_local(slot);
        let HeapReadOutput::Cell(mut cell) = this.heap.read(cell_id) else {
            panic!("DeleteCell: entry is not a Cell")

View on GitHub (pinned to adc986b362)

Solutions

  1. File a bug with the smallest Python snippet that triggers it
  2. Audit cell creation and `py_dec_ref_ids`/drop paths for premature frees of cell entries
  3. Run `cargo test -p monty --features memory-model-checks` on the relevant test binary to catch the refcount bug

Example fix

// not applicable — internal interpreter bug
Defensive patterns

Strategy: fallback

Try / catch

// Internal panic — no user-side catch; isolate and report.
match monty.run(code, limits) {
    Ok(res) => res,
    Err(e) => report_bug(code, e),
}

Prevention

When it happens

Trigger: Executing StoreCell (assigning to a free variable or a local captured by a nested function) when the cell HeapId resolves to a non-Cell heap entry; only via an interpreter bug or heap corruption.

Common situations: Fuzzing Monty; modifying cell allocation/freevariables handling; patches that free a cell and let the HeapId be reallocated as another type.

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