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
- Verify logging initialization completed fully (including sender publication) before calling flush.
- Check that no code path sets lifecycle to Running without publishing LOGGER_TX — this indicates an internal bug; report/reproduce with init logs.
- Reinitialize logging cleanly at process start and only call flush after successful init.
- 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
- Only call flush after logging_init returned Ok.
- Never manipulate logger lifecycle internals directly.
- Log or record init failures instead of ignoring them.
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
- Tracing subscriber already initialized
- Invalid spec pair: {kv}
- Logging has been shut down and cannot be re-initialized
- Global logging sender was already published
- Failed to initialize tracing subscriber: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/277f2388b5ad19ab.
Report an issue: GitHub.