{"record":{"id":"ae9fd6f5853b75f1","repo":"nautechsystems/nautilus_trader","slug":"registered-callback-lease-was-not-acquired","errorCode":null,"errorMessage":"registered callback lease was not acquired","messagePattern":"registered callback lease was not acquired","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/common/src/live/timer.rs","lineNumber":354,"sourceCode":"                        break;\n                    }\n                    None\n                };\n\n                if sender.is_some()\n                    && let Some(following_next_time_ns) = following_next_time_ns\n                {\n                    next_time_atomic\n                        .store(following_next_time_ns.as_u64(), atomic::Ordering::SeqCst);\n                }\n\n                match (&sender, &worker_dispatch) {\n                    (Some(sender), WorkerDispatch::Direct(factory)) => {\n                        sender.send(factory.message(event));\n                    }\n                    (Some(sender), WorkerDispatch::Registered(token)) => {\n                        let lease =\n                            registered_lease.expect(\"registered callback lease was not acquired\");\n\n                        if expires_after_fire {\n                            token.close();\n                        }\n                        sender.send(TimeEventMessage::registered(event, lease));\n                    }\n                    #[cfg(feature = \"python\")]\n                    (None, WorkerDispatch::SenderlessPython(callback)) => callback.call(event),\n                    _ => unreachable!(\"timer callback dispatch did not match its sender\"),\n                }\n\n                if sender.is_none()\n                    && let Some(following_next_time_ns) = following_next_time_ns\n                {\n                    next_time_atomic\n                        .store(following_next_time_ns.as_u64(), atomic::Ordering::SeqCst);\n                }\n","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/common/src/live/timer.rs#L336-L372","documentation":"`LiveTimer::start` splits dispatch into direct and registered-callback modes. For a registered callback (identified by token) it must have acquired a `registered_lease` from the callback registry; the code asserts this invariant with `.expect`. Missing lease means the timer was configured for a registered callback but the lease was never created/fetched before firing.","triggerScenarios":"Starting a live timer whose callback is a registered token while `registered_lease` is None — e.g. a senderless/registered-callback configuration path that skipped lease acquisition, or an internal race where the registry entry was removed before start.","commonSituations":"Custom timer setup mixing direct and registered callback APIs; global-runtime dispatch where lease acquisition is skipped; tests exercising registered callbacks after terminal events closed the registration.","solutions":["Ensure the registered callback is registered (lease acquired) before `set_timer_ns`/`set_time_alert_ns` fires","Use a direct (closure) callback instead of a registered token if you do not need named registration","Do not close/cancel the registration before the timer fires; close after fire as the code does via `token.close()`","Update/restart the timer after any registry change so a fresh lease is acquired"],"exampleFix":"// before\nlet token = registry.register(name, None); // no lease returned/held\ntimer.start(event);\n// after\nlet (token, lease) = registry.register(name, callback);\ntimer.start(event); // registered_lease = Some(lease)","handlingStrategy":"type-guard","validationCode":"if timer_is_registered_callback() && registered_lease.is_none() {\n    panic-avoid: re-acquire lease or fall back to direct callback before start();\n}","typeGuard":"fn ready_to_start(sender: &Option<Sender>, lease: &Option<Lease>, dispatch: &WorkerDispatch) -> bool {\n    !matches!(dispatch, WorkerDispatch::Registered(_)) || lease.is_some()\n}","tryCatchPattern":null,"preventionTips":["Register the callback and hold its lease before setting any timer that references it","Do not close/cancel registrations while timers referencing them are live","Use direct closure callbacks unless named registration is required","After cancelling registrations, recreate the timer rather than restarting it"],"tags":["rust","panic","timer","callback","async"],"backgroundTag":"internal-invariant-violation","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}