nautechsystems/nautilus_trader · error · anyhow::Error

Tracing subscriber already initialized

Error message

Tracing subscriber already initialized

What it means

init_tracing installs the process-global tracing subscriber exactly once, guarded by the TRACING_INITIALIZED atomic flag. If the subscriber has already been initialized (flag set), calling init_tracing again returns this error rather than re-installing or silently no-oping, because a global subscriber can only be set once per process.

Source

Thrown at crates/common/src/logging/bridge.rs:100

/// Initializes a tracing subscriber for external Rust crate logging.
///
/// This sets up a standard tracing subscriber that outputs to stdout with
/// the format controlled by `RUST_LOG` environment variable. The output
/// format uses nanosecond timestamps to align with Nautilus logging.
///
/// # Environment Variables
///
/// - `RUST_LOG`: Controls which modules emit tracing events and at what level.
///   - Example: `RUST_LOG=hyper=debug,tokio=warn`.
///   - Default: `warn` (if not set).
///
/// # Errors
///
/// Returns an error if the tracing subscriber has already been initialized.
pub fn init_tracing() -> anyhow::Result<()> {
    if TRACING_INITIALIZED.load(Ordering::SeqCst) {
        anyhow::bail!("Tracing subscriber already initialized");
    }

    let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warn"));

    let subscriber = tracing_subscriber::registry()
        .with(filter)
        .with(fmt::layer().event_format(NautilusFormatter));

    // Install only the tracing subscriber here. Python logging manages the
    // global `log` logger separately, so we must not claim it through
    // SubscriberInitExt::try_init().
    tracing::subscriber::set_global_default(subscriber)
        .map_err(|e| anyhow::anyhow!("Failed to initialize tracing subscriber: {e}"))?;

    TRACING_INITIALIZED.store(true, Ordering::SeqCst);
    Ok(())
}

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Call init_tracing only once at process startup; propagate the Result and skip on this specific error if double-init is expected.
  2. In tests, use a OnceGuard/OnceCell or an integration-test harness so only one test (or a shared once-guard) initializes tracing.
  3. If the host application already set a subscriber, remove the library-side init call instead of calling init_tracing.

Example fix

// before
setup()?; // calls init_tracing every time
init_tracing()?;
// after
static INIT: Once = Once::new();
INIT.call_once(|| {
    if let Err(e) = init_tracing() {
        eprintln!("tracing init skipped: {e}");
    }
});
Defensive patterns

Strategy: try-catch

Try / catch

// rust
match init_tracing() {
    Ok(()) => tracing::info!("tracing initialized"),
    Err(e) if e.to_string() == "Tracing subscriber already initialized" => {
        // expected in tests / multi-bootstrap: safe to ignore
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Calling init_tracing twice in the same process — e.g. once in a library/runner bootstrap and again in application main; re-running initialization in tests that share a process; restarting a subsystem that calls init as part of its setup.

Common situations: Test binaries where each #[test] calls a common setup helper that calls init_tracing; embedding the library in an app that already configured its own tracing subscriber; retry logic that re-invokes initialization after an unrelated failure.

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/bba2b17ad5655279. Report an issue: GitHub.