{"record":{"id":"ee3b56ea3116f46b","repo":"pydantic/monty","slug":"task-coroutine-id-doesn-t-point-to-a-coroutine","errorCode":null,"errorMessage":"task coroutine_id doesn't point to a Coroutine","messagePattern":"task coroutine_id doesn't point to a Coroutine","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/monty/src/bytecode/vm/async_exec.rs","lineNumber":455,"sourceCode":"    pub(super) fn handle_task_completion(&mut self, result: Value) -> Result<AwaitResult, RunError> {\n        let task_id = self\n            .scheduler\n            .current_task_id()\n            .expect(\"handle_task_completion called without current task\");\n        // Take the awaiter before cancelling the task: it owns the inc_ref on\n        // the gather it points at, so holding it here keeps that gather alive\n        // across the teardown below.\n        let task = self.scheduler.get_task_mut(task_id);\n        let awaiter = task.awaiter.take();\n        let coroutine_id = task\n            .coroutine_id\n            .expect(\"handle_task_completion: spawned task without a coroutine\");\n\n        // Mark the coroutine as Completed before the task is cancelled —\n        // direct `await` of this coroutine elsewhere needs to see the new\n        // state, not the `Running` it had until now.\n        let HeapReadOutput::Coroutine(mut coro) = self.heap.read(coroutine_id) else {\n            panic!(\"task coroutine_id doesn't point to a Coroutine\")\n        };\n        coro.get_mut(self.heap).state = CoroutineState::Completed;\n        drop(coro);\n\n        // Cancel the task now to release its inc_ref on the coroutine;\n        // otherwise it would linger in the scheduler. Its awaiter is already\n        // out, so this releases nothing the delivery below needs.\n        self.scheduler.cancel_task(task_id, self.heap);\n\n        // Hand the result down the chain. `None` means the gather that spawned\n        // this task settled first, so it ran on only for its side effects.\n        let delivery = if let Some(awaiter) = awaiter {\n            self.deliver_awaiter_success(awaiter, result)\n        } else {\n            result.drop_with(self);\n            None\n        };\n","sourceCodeStart":437,"sourceCodeEnd":473,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty/src/bytecode/vm/async_exec.rs#L437-L473","documentation":"This is an internal consistency panic in Monty's async scheduler: when a spawned task completes, handle_task_completion reads the task's coroutine from the heap and expects the stored HeapId to resolve to a Coroutine entry. If it resolves to any other heap type, the scheduler's internal bookkeeping is corrupted. It is not a Python-level error and cannot be triggered by valid user code.","triggerScenarios":"Only reachable if the task's coroutine_id field was mutated or the heap entry was freed and its slot reused for a non-Coroutine object, i.e. a reference-counting or GC bug in the interpreter itself.","commonSituations":"Developers hit this while hacking on monty's async runtime (async_exec.rs), after modifying task spawning/cancellation paths, or after changes to heap entry reuse without updating task state.","solutions":["Audit recent changes to handle_task_completion and task spawning for paths that overwrite task.coroutine_id","Check that task cancellation never dec_refs the coroutine while task.coroutine_id still points at it","Add a debug assertion when setting task.coroutine_id that the heap entry is a Coroutine","File a bug with a reproducing script if hit from normal async code"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat any such panic as an interpreter bug: capture the script and report it","Run async test suites with --features memory-model-checks when touching refcount-sensitive code","Avoid modifying task.coroutine_id outside load_or_init_task/init paths"],"tags":["async","internal-panic","scheduler"],"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-14T11:17:12.474Z"}