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
- Report the input/program to the Monty maintainers with a minimal reproducer — this indicates a heap type-tag invariant bug
- Check recent changes to heap allocation or list opcodes for places where a HeapId could be freed and reallocated as a non-List entry
- 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
- 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
- Keep monty updated; these panics are bugs to report, not user errors
- Attach the minimal Python snippet when filing the issue
- Run fuzz targets after touching heap or opcode code
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
- LoadCell: entry is not a Cell
- StoreCell: entry is not a Cell
- DeleteCell: entry is not a Cell
- StableHeap::get - {id:?} out of bounds
- LoadCell/StoreCell: expected cell reference in local slot {s
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/7a9daa9322290c1e.
Report an issue: GitHub.