{"record":{"id":"ba75046859ded46b","repo":"pydantic/monty","slug":"validated-gather-awaitable-is-heap-backed","errorCode":null,"errorMessage":"validated gather awaitable is heap-backed","messagePattern":"validated gather awaitable is heap-backed","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/monty/src/modules/asyncio.rs","lineNumber":120,"sourceCode":"    // so an invalid later argument needs no raw-HeapId rollback.\n    for arg in awaitables.iter() {\n        if !matches!(\n            arg,\n            Value::Ref(id)\n                if matches!(\n                    vm.heap.get(*id),\n                    HeapData::Coroutine(_) | HeapData::ExternalFuture(_) | HeapData::GatherFuture(_)\n                )\n        ) {\n            return Err(ExcType::type_error(\n                \"An asyncio.Future, a coroutine or an awaitable is required\",\n            ));\n        }\n    }\n\n    let items = awaitables\n        .drain(..)\n        .map(|arg| arg.into_ref_id().expect(\"validated gather awaitable is heap-backed\"))\n        .collect();\n    let gather_future = GatherFuture::new(items);\n    let id = vm.heap.allocate(HeapData::GatherFuture(Box::new(gather_future)));\n    Ok(Value::Ref(id))\n}\n\n/// `asyncio.gather(*awaitables)` — variadic positional, no kwargs accepted.\n///\n/// `kwargs_not_supported_yet` rejects any kwarg with the macro's\n/// \"not yet implemented\" `NotImplementedError`, replacing the previous\n/// `TypeError: gather() takes no keyword arguments` from `into_pos_only`.\n/// When CPython's `return_exceptions=False` is wired up this becomes a\n/// regular `kw_only` slot and the flag goes away.\n#[derive(FromArgs)]\n#[from_args(name = \"gather\", kwargs_not_supported_yet)]\nstruct GatherArgs {\n    #[from_args(varargs)]\n    awaitables: Vec<Value>,","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty/src/modules/asyncio.rs#L102-L138","documentation":"`gather` validates its awaitable arguments earlier (rejecting non-heap objects), so `into_ref_id` is expected to succeed; this `expect` panics if a validated argument is not a heap reference. It encodes the invariant 'validation already ran, so every remaining item is a Ref'.","triggerScenarios":"Calling `asyncio.gather` in Monty code with an awaitable whose underlying `Value` is not a heap-backed `Value::Ref` but somehow passed the preceding validation — an interpreter bug, not user-reachable through normal Python code.","commonSituations":"Only from internal validation/refactoring drift: someone changes or reorders the validation loop and misses a non-ref case.","solutions":["Check the validation block above this line covers every accepted awaitable type.","Convert the expect into an explicit internal error if new value kinds are added.","Report as a bug with the gather call that triggered it."],"exampleFix":"// before\n.map(|arg| arg.into_ref_id().expect(\"validated gather awaitable is heap-backed\"))\n// after\n.map(|arg| arg.into_ref_id().map_err(|_| VmError::internal(\"gather awaitable not heap-backed\")))\n.collect::<RunResult<Vec<_>>>()?;","handlingStrategy":"try-catch","validationCode":"# In Monty Python code, only pass real awaitables to asyncio.gather\nassert all(hasattr(a, '__await__') for a in awaitables)","typeGuard":null,"tryCatchPattern":"// Host-side (Rust embedding monty)\nmatch result {\n    Err(MontyError::Panic(msg)) if msg.contains(\"heap-backed\") => log_internal_bug(msg),\n    other => other?,\n}","preventionTips":["Keep the validation loop above this line in sync with accepted types","Add a unit test for each accepted awaitable kind","Treat occurrence as a regression"],"tags":["rust","panic","asyncio"],"backgroundTag":"internal-invariant-violation","analyzedSha":"adc986b362e3961f407868cb118a99fe831b9e61","analyzedAt":"2026-09-13T19:19:18.698Z","contentChangedAt":"2026-09-13T19:19:18.698Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}