nautechsystems/nautilus_trader · critical
Component '{id}' is already mutably borrowed. This would cre
Error message
Component '{id}' is already mutably borrowed. This would create aliasing mutable references (undefined behavior). What it means
The global component registry enforces single mutable borrow of each registered component via a try_borrow flag. start_component first try_borrows the component; if it is already mutably borrowed (another lifecycle call in progress), it rejects with this message to prevent aliasing &mut references, which would be undefined behavior in Rust.
Source
Thrown at crates/common/src/component.rs:554
component_ref
}
/// Safely calls `start()` on a component in the global registry.
///
/// # Errors
///
/// - Returns an error if the component is not found.
/// - Returns an error if the component is already borrowed.
/// - Returns an error if `start()` fails.
pub fn start_component(id: &Ustr) -> anyhow::Result<()> {
let component_ref = with_component_registry(|registry| {
let component_ref = registry
.get(id)
.ok_or_else(|| anyhow::anyhow!("Component '{id}' not found in global registry"))?;
if !registry.try_borrow(*id) {
anyhow::bail!(
"Component '{id}' is already mutably borrowed. \
This would create aliasing mutable references (undefined behavior)."
);
}
Ok::<_, anyhow::Error>(component_ref)
})?;
let _guard = BorrowGuard::new(*id);
// SAFETY: Borrow tracking ensures exclusive access
unsafe {
let component = &mut *component_ref.get();
component.start()
}
}
/// Returns the state of a component in the global registry.View on GitHub (pinned to 18893faf8b)
Solutions
- Never call start_component (or other lifecycle functions) on a component from within that component's own callbacks.
- Ensure the borrow is released before the next call: lifecycle helpers release on scope exit and on panic; check that no closure still holds the returned reference.
- Serialize lifecycle operations per component with your own lock/queue so only one runs at a time.
- Restructure so the component signals completion (e.g. via message/event) and the caller starts it afterwards, instead of nested calls.
Example fix
// before
fn on_start(&mut self) {
start_component(&self.id()); // self is already borrowed -> UB guard fires
}
// after
fn on_start(&mut self) {
// schedule external start instead of borrowing self
self.msgbus.publish_start_request(&self.id());
} Defensive patterns
Strategy: try-catch
Validate before calling
fn is_lifecycle_idle(component_id: &ComponentId) -> bool {
// ensure no lifecycle task is in flight for this id before starting
LIFECYCLE_IN_FLIGHT.get(component_id).map_or(true, |b| !*b)
} Try / catch
match start_component(id) {
Err(e) if e.to_string().contains("already mutably borrowed") => {
log::warn!("component {id} busy; retrying after in-flight op");
}
other => other?,
} Prevention
- Never start a component from inside its own callbacks (no re-entrancy)
- Serialize lifecycle calls per component id
- Do not hold the returned &mut across other registry calls
- Release borrows promptly; scope the reference tightly
When it happens
Trigger: Calling start_component for a component id while another lifecycle operation (start/stop/reset/dispose or component_state access that is still in scope) holds the borrow — e.g. calling start from inside the component's own start() callback, or concurrent start_component calls for the same id.
Common situations: Re-entrant lifecycle calls: a component's on_start handler calling start_component on itself; background threads triggering start while the main loop already borrowed it; nested strategies starting their parent; deadlock-avoidance tests.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Invalid state trigger {self} -> {trigger}
- Active execution intent {intent_id} was not found
- DataActor {} must be registered before calling `clock_mut()`
- DataActor {} must be registered before calling `clock()` - t
- DataActor {} must be registered before calling `clock_ref()`
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/f18d1faabec21dfd.
Report an issue: GitHub.