{"record":{"id":"daa22f10be59c81e","repo":"nautechsystems/nautilus_trader","slug":"invalid-state-trigger-self-trigger","errorCode":null,"errorMessage":"Invalid state trigger {self} -> {trigger}","messagePattern":"Invalid state trigger (.+?) -> (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/common/src/component.rs","lineNumber":402,"sourceCode":"            (Self::Resuming, ComponentTrigger::Stop) => Self::Stopping,\n            (Self::Resuming, ComponentTrigger::ResumeCompleted) => Self::Running,\n            (Self::Resuming, ComponentTrigger::Fault) => Self::Faulting,\n            (Self::Stopping, ComponentTrigger::StopCompleted) => Self::Stopped,\n            (Self::Stopping, ComponentTrigger::Dispose) => Self::Disposing,\n            (Self::Stopping, ComponentTrigger::Fault) => Self::Faulting,\n            (Self::Stopped, ComponentTrigger::Reset) => Self::Resetting,\n            (Self::Stopped, ComponentTrigger::Resume) => Self::Resuming,\n            (Self::Stopped, ComponentTrigger::Dispose) => Self::Disposing,\n            (Self::Stopped, ComponentTrigger::Fault) => Self::Faulting,\n            (Self::Degrading, ComponentTrigger::DegradeCompleted) => Self::Degraded,\n            (Self::Degraded, ComponentTrigger::Resume) => Self::Resuming,\n            (Self::Degraded, ComponentTrigger::Stop) => Self::Stopping,\n            (Self::Degraded, ComponentTrigger::Fault) => Self::Faulting,\n            (Self::Disposing, ComponentTrigger::DisposeCompleted) => Self::Disposed,\n            (Self::Disposing, ComponentTrigger::Fault) => Self::Faulting,\n            (Self::Faulting, ComponentTrigger::Dispose) => Self::Disposing,\n            (Self::Faulting, ComponentTrigger::FaultCompleted) => Self::Faulted,\n            _ => anyhow::bail!(\"Invalid state trigger {self} -> {trigger}\"),\n        };\n        Ok(new_state)\n    }\n}\n\nthread_local! {\n    static COMPONENT_REGISTRY: ComponentRegistry = ComponentRegistry::new();\n}\n\n/// Registry for storing components with runtime borrow tracking.\n///\n/// The registry tracks which components are currently mutably borrowed to prevent\n/// multiple simultaneous mutable borrows (which would be undefined behavior).\npub struct ComponentRegistry {\n    components: RefCell<AHashMap<Ustr, Rc<UnsafeCell<dyn Component>>>>,\n    borrows: RefCell<AHashSet<Ustr>>,\n}\n","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/common/src/component.rs#L384-L420","documentation":"ComponentState::transition implements an explicit finite state machine for component lifecycles (PreInitialized, Ready, Starting, Running, Stopping, Degraded, Faulting, Disposing, etc.). Any (state, trigger) pair not in the table is invalid — e.g. triggering Fault on a Running component without going through Stopping first — and the call bails. This protects components from illegal lifecycle jumps.","triggerScenarios":"Calling component transition APIs (stop/fault/dispose) in an order the state machine does not allow, e.g. sending ComponentTrigger::Fault while the component is in a state with no Fault edge, or double-triggering a transition (stop twice, dispose twice).","commonSituations":"Race conditions where two lifecycle calls interleave (a stop callback firing while fault handling is in progress); retrying a failed stop and hitting an unexpected state; custom actors/strategies overriding callbacks and triggering transitions manually; shutdown sequences disposing components that already faulted.","solutions":["Inspect the component's current state (component.state()) before issuing a transition trigger and only send triggers with a valid edge from that state.","Use the higher-level helpers (stop_component, dispose_component, reset_component) instead of raw transition calls; they handle state checks.","Serialize lifecycle calls — do not trigger transitions concurrently from multiple tasks/threads.","Check prior error logs: an earlier fault may have moved the component into Faulted/Faulting, making subsequent triggers invalid; reset or dispose from there."],"exampleFix":"// before\nif let Err(e) = component.transition(ComponentTrigger::Fault) { /* Fault from Running is invalid */ }\n// after\nlet next = component.state().transition(ComponentTrigger::Stop); // or use stop_component helper\nif next.is_ok() {\n    component.transition(ComponentTrigger::Stop);\n} else {\n    log::warn!(\"Skipping transition: {:?}\", next.err());\n}","handlingStrategy":"try-catch","validationCode":"fn can_transition(state: ComponentState, trigger: ComponentTrigger) -> bool {\n    state.transition(trigger).is_ok()\n}","typeGuard":null,"tryCatchPattern":"match component.transition(trigger) {\n    Ok(new_state) => log::debug!(\"transitioned to {new_state}\"),\n    Err(e) if e.to_string().starts_with(\"Invalid state trigger\") => {\n        log::warn!(\"ignoring invalid lifecycle trigger: {e}\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Inspect component.state() before issuing any trigger","Use high-level helpers (start/stop/dispose_component) instead of raw transitions","Never interleave lifecycle triggers concurrently on one component","Consult the state table in component.rs for valid (state, trigger) pairs before adding custom transition logic"],"tags":["state-machine","lifecycle","component","invalid-transition"],"backgroundTag":"invalid-state-transition","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}