{"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":397,"sourceCode":"            (Self::Running, ComponentTrigger::Degrade) => Self::Degrading,\n            (Self::Running, ComponentTrigger::Fault) => Self::Faulting,\n            (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::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::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":379,"sourceCodeEnd":415,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/2114cf6f761429e0adb5ca9596fcd7b895b16011/crates/common/src/component.rs#L379-L415","documentation":"Every actor, strategy, data/execution client, and engine in NautilusTrader is a Component governed by a strict lifecycle state machine (ComponentState: PreInitialized, Ready, Starting, Running, Stopping, Stopped, Degraded, Faulting, Faulted, Disposing, Disposed, and transitional states). ComponentState::transition in crates/common/src/component.rs:368 only accepts the enumerated (state, trigger) pairs and rejects every other combination. This error means a lifecycle command (initialize/start/stop/resume/reset/degrade/fault/dispose) was issued from a state where that trigger is not defined, for example Start while already Running, or Resume while Ready.","triggerScenarios":"Calling component.start() when the state is anything other than Ready (e.g. a second start() on a Running component); stop() on a PreInitialized or Ready component (Stop is only valid from Starting/Running/Resuming/Degraded); resume() on Running or Ready (only valid from Stopped/Degraded); reset() while Running (only valid from Ready/Stopped); dispose() while Running (only from Ready/Stopped); or issuing any non-completion trigger while inside a transitional state such as Starting, Stopping, Resetting, Degrading, Resuming, Disposing (which accept only their Completed trigger, plus Stop/Fault where listed).","commonSituations":"Custom actors/strategies that call self.stop() or self.degrade() inside handlers while the trader also stops them at shutdown; tests that reuse one component instance and call start() twice; calling resume() on a component that was never stopped; trying to restart a Faulted or Disposed component instead of building a new one; lifecycle commands fired concurrently from multiple timers or tasks.","solutions":["Log or inspect component.state() before issuing the lifecycle command and only send a trigger valid for that state (start only from Ready, resume only from Stopped/Degraded, reset from Ready/Stopped).","Guard calls with the provided predicates: is_running(), is_stopped(), is_degraded(), is_faulted(), is_disposed(), not_running().","To reuse a stopped component, call reset() (Stopped -> Resetting -> Ready) before start(); never reuse a Disposed or Faulted component, construct a new one.","Audit custom on_start/on_stop handlers for internal lifecycle calls that race with the trader's own stop/start sequence.","Serialize lifecycle commands through a single task or timer so triggers cannot interleave inside transitional states."],"exampleFix":"// before\nif let Err(e) = actor.start() {\n    log::error!(\"start failed: {e}\");\n}\n\n// after\nuse nautilus_trader::common::component::{Component, ComponentState};\nif actor.state() == ComponentState::Ready {\n    actor.start()?;\n} else {\n    log::debug!(\"skipped start, state = {:?}\", actor.state());\n}","handlingStrategy":"validation","validationCode":"use nautilus_trader::common::component::{Component, ComponentState};\n\n// Only start from Ready; only resume from Stopped/Degraded\nif actor.state() == ComponentState::Ready {\n    actor.start()?;\n}\nif actor.is_stopped() || actor.is_degraded() {\n    actor.resume()?;\n}","typeGuard":"fn trigger_is_valid(state: ComponentState, trigger: ComponentTrigger) -> bool {\n    use ComponentState::*;\n    use ComponentTrigger::*;\n    matches!(\n        (state, trigger),\n        (PreInitialized, Initialize)\n            | (Ready, Reset | Start | Dispose)\n            | (Resetting, ResetCompleted)\n            | (Starting, StartCompleted | Stop | Fault)\n            | (Running, Stop | Degrade | Fault)\n            | (Resuming, Stop | ResumeCompleted | Fault)\n            | (Stopping, StopCompleted | Fault)\n            | (Stopped, Reset | Resume | Dispose | Fault)\n            | (Degrading, DegradeCompleted)\n            | (Degraded, Resume | Stop | Fault)\n            | (Disposing, DisposeCompleted | Fault)\n            | (Faulting, FaultCompleted)\n    )\n}","tryCatchPattern":"if let Err(e) = component.start() {\n    if e.to_string().contains(\"Invalid state trigger\") {\n        log::warn!(\n            \"lifecycle rejected: state={:?}, retry after checking is_ready\",\n            component.state()\n        );\n    } else {\n        return Err(e);\n    }\n}","preventionTips":["Check state()/is_running()/is_stopped() predicates before every lifecycle call.","Never call start()/stop() from inside on_start/on_stop handlers of the same component.","Drive lifecycle commands from a single owner (one task/timer) to avoid interleaved triggers.","Reset (not restart) stopped components, and construct new instances for Faulted/Disposed ones."],"tags":["component","state-machine","lifecycle","rust"],"backgroundTag":"invalid-state-transition","analyzedSha":"2114cf6f761429e0adb5ca9596fcd7b895b16011","analyzedAt":"2026-08-21T11:28:30.864Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}