nautechsystems/nautilus_trader · error · anyhow::Error
failed to request logging sync: {e}
Error message
failed to request logging sync: {e} What it means
`sync_sender_to_disk` sends a `LogEvent::Sync` oneshot handshake to the logger thread to flush buffered log events, then waits for the acknowledgement. If the `tx.send` fails — meaning the logging thread's receiver was dropped (logger thread exited/shut down) or the channel is broken — this error is returned.
Source
Thrown at crates/common/src/logging/logger.rs:1315
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.
pub fn log<T: AsRef<str>>(level: LogLevel, color: LogColor, component: Ustr, message: T) {
let color = Value::from(color as u8);
match level {
LogLevel::Off => {}
LogLevel::Trace => {
log::trace!(component = component.to_value(), color = color; "{}", message.as_ref());
}
LogLevel::Debug => {
log::debug!(component = component.to_value(), color = color; "{}", message.as_ref());
}View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure the LogGuard outlives all flush calls; flush before dropping the guard.
- Treat send-failure as 'logging already shut down' — handle gracefully and skip flushing instead of propagating.
- Check whether the logging thread panicked (look for earlier panics) and fix the event payload or sink that caused it.
- Sequence shutdown: flush logs first, then drop guard, then exit; avoid flushing from atexit/Destructor after teardown.
Example fix
// before drop(guard); logger.flush()?; // failed to request logging sync // after guard.flush()?; // flush while logging thread is alive // then drop(guard);
Defensive patterns
Strategy: try-catch
Validate before calling
// Rust: confirm the logging thread is alive before flushing
if !logging_thread_running() { return Ok(()); } // nothing to flush Type guard
fn can_flush(tx: &Sender<LogEvent>) -> bool { !tx.is_closed() } // wrapper with capacity probe Try / catch
match sync_sender_to_disk(&tx) {
Err(e) if e.to_string().contains("failed to request logging sync") => {
debug!("logger already shut down; skipping flush");
}
other => other?,
} Prevention
- Flush logs before dropping the LogGuard, while the logger thread is alive.
- Order shutdown explicitly: flush -> drop guard -> process exit.
- Avoid flushing from atexit/destructors or other threads after teardown.
- Investigate logger-thread panics; a dead thread makes every later flush fail.
When it happens
Trigger: Calling flush/sync (logger flush API or guard drop-time flush) after the logging thread has already terminated: guard dropped while another reference triggers flush, logger thread panicked, or flush invoked during shutdown ordering races.
Common situations: Calling logger.flush() after shutdown/exit handlers ran; dropping the LogGuard then flushing from another thread; panics in the logging thread from malformed events; multi-node processes where one node's teardown closed the shared sender.
Related errors
- failed to receive logging sync acknowledgement: {e}
- Failed to send order updated event: {e}
- Failed to send order canceled event: {e}
- Failed to send order rejected event: {e}
- Failed to send delete order command: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/67ece42a7f818453.
Report an issue: GitHub.