ITADN

Heap::dec_ref: object already freed under sustained MontyRepl.feed_run() pressure

#425Opendbreunig 创建于 2026-05-09
D
dbreunigcommented
# `Heap::dec_ref: object already freed` under sustained `MontyRepl.feed_run()` pressure ## Summary `MontyRepl` aborts the host process with a Rust panic during a heavy aggregation loop over a deeply-nested injected data structure. The panic is on Monty's internal heap reference counting, originates inside the interpreter, and cannot be caught from Python (`MontyRuntimeError` is not raised — the process aborts). ``` thread '<unnamed>' panicked at crates/monty/src/heap.rs:938:34: HeapEntries::get - data already freed thread '<unnamed>' panicked at crates/monty/src/heap.rs:956:39: Heap::dec_ref: object already freed panic in a destructor during cleanup thread caused non-unwinding panic. aborting. ``` Reproduces on the latest release (`pydantic-monty 0.0.17`) and on `0.0.15`. The GC-related fixes in 0.0.17 (#371, #376, #381) do not address it. [Here is a script to reproduce the error](https://gist.github.com/dbreunig/6daa83efb2ae08f8357172ce1124f675). ## Environment - `pydantic-monty`: **0.0.17** (also reproduced on 0.0.15) - Python: **3.12.4** - OS: **macOS 15.7.3, arm64 (Apple Silicon)** - Install: `pip install pydantic-monty` (PyPI wheel) ## Expected behavior `MontyRepl.feed_run()` should either succeed, or raise a catchable `MontyRuntimeError` if a resource limit is exceeded. It should never abort the host process from Rust. ## What seems to drive it Heap pressure across multiple `feed_run()` calls against a persistent REPL, not raw record count alone. Bisecting the repro: - **String variety matters more than `NUM_RECORDS`.** An earlier version used 8 fixed strings via `random.choice`; the panic did not reproduce even at 100K records. Switching to template-formatted, mostly-unique strings reproduced consistently. - **The deliberately-failing `iter 0`** (`from collections import Counter`, which Monty rejects) appears to participate. It walks Monty's exception/unwind path before the heavy iteration runs. Whether the panic still fires without it is worth confirming; in our hands, the combination above panics every run. - **Repeating the heavy aggregator** (`REPEATS = 3`) keeps `monthly` and its nested dicts alive across calls, growing the heap further between iterations. ## What we ruled out - Not a user-code error — the symptoms (`MontySyntaxError`, `MontyRuntimeError`) would surface as catchable Python exceptions. - Not specific to any embedding/adapter (e.g. DSPy) — repro uses `MontyRepl` directly. - Not the `0.0.17` GC fixes (#371, #376, #381) — same panic before and after upgrading. ## Notes for triage - Panic message `Heap::dec_ref: object already freed` plus the destructor-cleanup secondary panic is a refcount-management bug rather than a resource-limit issue. - `HeapEntries::get - data already freed` at `heap.rs:938` and `Heap::dec_ref: object already freed` at `heap.rs:956` are the right pointers in the source. And yes: I ran into this error while running an RLM 😉
0 条评论