nautechsystems/nautilus_trader · error · anyhow::Error

Logging is running without a published sender

Error message

Logging is running without a published sender

What it means

The flush-to-disk routine only runs while the logger lifecycle is Running and requires the global LOGGER_TX sender to be present. If the lifecycle says Running but the sender slot was never populated (or was consumed), the internal invariant is broken and this error is raised instead of silently skipping the flush.

Source

Thrown at crates/common/src/logging/logger.rs:1304

/// # Errors
///
/// Returns an error if the sync request cannot be delivered or acknowledged.
pub fn sync_to_disk() -> anyhow::Result<()> {
    #[cfg(all(feature = "simulation", madsim))]
    {
        Ok(())
    }

    #[cfg(not(all(feature = "simulation", madsim)))]
    {
        let lifecycle = LOGGER_LIFECYCLE.lock();

        if *lifecycle != LoggerLifecycle::Running {
            return Ok(());
        }

        let Some(tx) = LOGGER_TX.get() else {
            anyhow::bail!("Logging is running without a published sender");
        };

        sync_sender_to_disk(tx)
    }
}

#[cfg(not(all(feature = "simulation", madsim)))]
fn sync_sender_to_disk(tx: &std::sync::mpsc::Sender<LogEvent>) -> anyhow::Result<()> {
    let (done_tx, done_rx) = std::sync::mpsc::channel();
    tx.send(LogEvent::Sync(done_tx))
        .map_err(|e| anyhow::anyhow!("failed to request logging sync: {e}"))?;

    done_rx
        .recv()
        .map_err(|e| anyhow::anyhow!("failed to receive logging sync acknowledgement: {e}"))?
}

/// Logs a message with the given level, color, and component.

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify logging initialization completed fully (including sender publication) before calling flush.
  2. Check that no code path sets lifecycle to Running without publishing LOGGER_TX — this indicates an internal bug; report/reproduce with init logs.
  3. Reinitialize logging cleanly at process start and only call flush after successful init.
  4. As a defensive measure, treat flush errors as non-fatal in application shutdown handlers, but fix the underlying init ordering.

Example fix

// before
logging_init(cfg).ok(); // ignore init failure
logging_flush()?; // bails: no sender
// after
logging_init(cfg)?; // propagate init errors
logging_flush()?;
Defensive patterns

Strategy: try-catch

Validate before calling

fn flush_safe() -> anyhow::Result<()> {
    // only flush after a confirmed successful init
    logging_flush()
}

Try / catch

if let Err(e) = logging_flush() {
    if e.to_string().contains("without a published sender") {
        eprintln!("flush skipped: logger not fully initialized");
    } else {
        return Err(e);
    }
}

Prevention

When it happens

Trigger: Calling the flush/sync-to-disk method while LOGGER_TX.get() returns None even though lifecycle == Running — e.g. after a corrupted or partial initialization sequence.

Common situations: Custom or embedded integration that manipulated the logger lifecycle directly, a race between init and an early flush call, or library misuse of internal logging APIs.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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