pydantic/monty · critical

list_extend: expected List on heap

Error message

list_extend: expected List on heap

What it means

This is an internal panic in Monty's list extension path: `list_extend` looked up the target list's HeapId in the heap and found an entry that is not a `List`. The VM tracks lists as `Value::Ref(id)` with the heap entry type fixed at allocation time, so this can only happen if the heap entry type and the stack value disagree — an internal invariant violation, not a user-facing Python error.

Source

Thrown at crates/monty/src/bytecode/vm/collections.rs:127

            let type_ = iterable.py_type_name(this);
            return Err(if opts_out_of_iter(iterable, this) {
                ExcType::type_error_not_iterable(&type_)
            } else {
                ExcType::type_error_value_after_star(&type_)
            });
        }

        {
            let copied_items: Vec<Value> = collect_iterable(iterable, this)?;
            defer_drop_mut!(copied_items, this);

            // Check if any copied items are refs (for updating contains_refs)
            let has_refs = copied_items.iter().any(|v| matches!(v, Value::Ref(_)));

            // Extend the list
            if let Value::Ref(id) = list_ref {
                let HeapReadOutput::List(mut list) = this.heap.read(*id) else {
                    panic!("list_extend: expected List on heap");
                };
                let list = list.get_mut(this.heap);
                // Update contains_refs before extending
                if has_refs {
                    list.set_contains_refs();
                }
                list.as_vec_mut().append(copied_items);
            }
        }

        // Push list_ref back on the stack (don't drop it)
        let (list_ref, this) = list_ref_guard.into_parts();
        this.push(list_ref);
        Ok(())
    }

    /// Converts a list to a tuple.
    ///

View on GitHub (pinned to adc986b362)

Solutions

  1. Report the input/program to the Monty maintainers with a minimal reproducer — this indicates a heap type-tag invariant bug
  2. Check recent changes to heap allocation or list opcodes for places where a HeapId could be freed and reallocated as a non-List entry
  3. Run `make test-memory-model-checks` (or the relevant test binary with `--features memory-model-checks`) to surface the refcount/drop bug causing the mismatch
  4. As a workaround, reduce the Python program to the smallest snippet that triggers it and avoid that construct until fixed

Example fix

// not applicable — bug is inside crates/monty/src/bytecode/vm/collections.rs;
// the panic already documents the violated assumption
Defensive patterns

Strategy: fallback

Try / catch

// Panics are not catchable via RunResult; treat any panic as a monty bug.
// Isolate executions so a panic cannot take down the host (e.g. run in a
// worker subprocess, as monty-pool/monty subprocess already do).
match monty.run(code, limits) {
    Ok(res) => res,
    Err(e) => report_bug(e),
}

Prevention

When it happens

Trigger: Calling Python code that extends a list (e.g. `list.extend`, `+=` on a list, or the ListExtend opcode) when the heap entry behind the list's HeapId has been corrupted, freed and reallocated as another type, or when a bug mis-tags a heap entry; effectively only reachable via a monty interpreter bug or fuzzing, not via ordinary API use.

Common situations: Running fuzz targets (crates/fuzz) over Monty; developing a new opcode or heap type in monty that allocates or re-tags heap entries; misuse of low-level Heap APIs in a patch that lets a HeapId be reused for a different HeapData variant.

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