{"record":{"id":"cc9cf0694a23575d","repo":"nautechsystems/nautilus_trader","slug":"python-on-dispose-failed-e-cc9cf0","errorCode":null,"errorMessage":"Python on_dispose failed: {e}","messagePattern":"Python on_dispose failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/trading/src/python/algorithm.rs","lineNumber":570,"sourceCode":"        self.dispatch_no_args(\"on_stop\")\n            .map_err(|e| anyhow::anyhow!(\"Python on_stop failed: {e}\"))\n    }\n\n    fn on_resume(&mut self) -> anyhow::Result<()> {\n        ExecutionAlgorithm::on_resume(self)?;\n        self.dispatch_no_args(\"on_resume\")\n            .map_err(|e| anyhow::anyhow!(\"Python on_resume failed: {e}\"))\n    }\n\n    fn on_reset(&mut self) -> anyhow::Result<()> {\n        ExecutionAlgorithm::on_reset(self)?;\n        self.dispatch_no_args(\"on_reset\")\n            .map_err(|e| anyhow::anyhow!(\"Python on_reset failed: {e}\"))\n    }\n\n    fn on_dispose(&mut self) -> anyhow::Result<()> {\n        self.dispatch_no_args(\"on_dispose\")\n            .map_err(|e| anyhow::anyhow!(\"Python on_dispose failed: {e}\"))\n    }\n\n    fn on_degrade(&mut self) -> anyhow::Result<()> {\n        self.dispatch_no_args(\"on_degrade\")\n            .map_err(|e| anyhow::anyhow!(\"Python on_degrade failed: {e}\"))\n    }\n\n    fn on_fault(&mut self) -> anyhow::Result<()> {\n        self.dispatch_no_args(\"on_fault\")\n            .map_err(|e| anyhow::anyhow!(\"Python on_fault failed: {e}\"))\n    }\n\n    fn on_time_event(&mut self, event: &TimeEvent) -> anyhow::Result<()> {\n        ExecutionAlgorithm::on_time_event(self, event)?;\n        self.dispatch_time_event(event)\n            .map_err(|e| anyhow::anyhow!(\"Python on_time_event failed: {e}\"))\n    }\n","sourceCodeStart":552,"sourceCodeEnd":588,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/trading/src/python/algorithm.rs#L552-L588","documentation":"PyExecutionAlgorithm's DataActor on_dispose dispatches the Python `on_dispose` callback directly (no Rust pre-step); any Python exception is re-wrapped with this anyhow message. Dispose releases the component's resources, so this error means cleanup code failed in Python.","triggerScenarios":"Calling on_dispose when the Python on_dispose raises — e.g. freeing resources twice, or bad attribute access during teardown.","commonSituations":"Double dispose when both stop and dispose paths run cleanup; dropping references already released; exceptions in __del__-like cleanup logic.","solutions":["Read the chained `{e}` traceback and fix the Python on_dispose implementation.","Make dispose idempotent (set released handles to None and check before use).","Ensure dispose is called at most once per lifecycle; track a disposed flag if needed.","Verify the zero-arg `def on_dispose(self)` signature."],"exampleFix":"// before\ndef on_dispose(self):\n    self.cache.close()  # second call raises\n\n// after\ndef on_dispose(self):\n    if self.cache is not None:\n        self.cache.close()\n        self.cache = None","handlingStrategy":"try-catch","validationCode":"# Python: guard against double dispose\nif getattr(self, \"disposed\", False): return","typeGuard":null,"tryCatchPattern":"if let Err(e) = actor.on_dispose() {\n    log::error!(\"python on_dispose failed: {e:#}\"); // release handles regardless\n}","preventionTips":["Set a disposed flag and no-op subsequent on_dispose calls.","Null out released handles so repeated cleanup cannot raise.","Never rely on __del__ for the same resources on_dispose releases."],"tags":["rust","python","pyo3","lifecycle","on-dispose"],"backgroundTag":"api-error-response","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}