atuinsh/atuin · error
failed to register sigterm handler
Error message
failed to register sigterm handler
What it means
At startup, atuin-daemon registers a SIGTERM listener with tokio::signal::unix::signal(SignalKind::terminate()) inside shutdown_signal(). Registration fails when the current tokio runtime has no IO/signal driver enabled, or when OS-level setup fails (e.g. resource exhaustion). The expect converts registration failure into an immediate daemon panic.
Source
Thrown at crates/atuin-daemon/src/lib.rs:120
handle,
)
.await?;
// Run the daemon event loop
daemon.run_event_loop().await?;
// Stop all components on shutdown
daemon.stop_components().await;
tracing::info!("daemon shut down complete");
Ok(())
}
/// Wait for a shutdown signal (Ctrl+C or SIGTERM).
#[cfg(unix)]
async fn shutdown_signal() {
let mut term = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.expect("failed to register sigterm handler");
let mut int = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::interrupt())
.expect("failed to register sigint handler");
tokio::select! {
_ = term.recv() => {},
_ = int.recv() => {},
}
}
/// Wait for a shutdown signal (Ctrl+C).
#[cfg(not(unix))]
async fn shutdown_signal() {
tokio::signal::ctrl_c()
.await
.expect("failed to listen for ctrl+c");
}
View on GitHub (pinned to 202f6ad98e)
Solutions
- Ensure the daemon runs inside a tokio runtime built with enable_all() — the shipped atuin binary does this
- Raise fd/memory limits if registration fails at boot (ulimit -n, systemd LimitNOFILE)
- When embedding, register signals inside the runtime context, never from a bare executor
Example fix
// before
let rt = tokio::runtime::Builder::new_multi_thread().build()?;
// after — signal handlers need the IO driver
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?; Defensive patterns
Strategy: validation
Validate before calling
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all() // installs the IO/signal driver signal() requires
.build()?;
rt.block_on(async {
// daemon code, including shutdown_signal()
}); Try / catch
match tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) {
Ok(term) => tokio::select! { _ = term.recv() => {} },
Err(e) => {
tracing::error!("sigterm handler unavailable: {e}; relying on SIGINT");
tokio::signal::ctrl_c().await.ok();
}
} Prevention
- Build every runtime that awaits signal APIs with enable_all()
- Register exactly one shutdown-signal listener per process
- Test daemon boot under the production container's fd and memory limits
When it happens
Trigger: Reaching daemon shutdown_signal() inside a tokio runtime built without .enable_all()/.enable_io(); hitting EMFILE or memory limits while tokio installs its signal machinery; a sandbox policy blocking sigaction/eventfd setup.
Common situations: Embedding atuin-daemon's run path in a custom runtime (current_thread without enable_io); heavily loaded boxes at fd limits; restrictive seccomp profiles.
Related errors
- failed to register sigint handler
- failed to listen for ctrl+c
- failed to register signal handler
- bug in list query. please report
- bug in search query. please report
AI-assisted analysis of atuinsh/atuin@202f6ad98e (2026-08-16).
Data as JSON: /api/errors/c5b66df5648797d3.
Report an issue: GitHub.