atuinsh/atuin · error

failed to register sigterm handler

Error message

failed to register sigterm handler

What it means

`shutdown_signal()` in the Atuin daemon registers a SIGTERM handler via `tokio::signal::unix::signal`. Panicking here means the OS refused to install the signal handler — the signal event source could not be created. Without it the daemon cannot shut down gracefully on SIGTERM.

Solutions

  1. Ensure the daemon runs inside a properly configured tokio multi-thread runtime with the `signal` feature
  2. Check `ulimit -i` (RLIMIT_SIGPENDING) and raise it if exhausted
  3. Investigate leaked signal handler registrations in long-running wrapper processes
  4. Update tokio — older versions had signal-driver edge cases

Example fix

// before
#[tokio::main]
async fn main() { /* runtime without io/signal driver */ }
// after
#[tokio::main(flavor = "multi_thread")]
async fn main() { /* ensure tokio features "rt-multi-thread", "signal", "macros" */ }
Defensive patterns

Strategy: try-catch

Try / catch

// Use the Result-returning API instead of expect:
let mut term = tokio::signal::unix::signal(SignalKind::terminate())
    .map_err(|e| BootError::SignalHandler(e))?;

Prevention

When it happens

Trigger: `SignalKind::terminate()` handler registration failing inside `shutdown_signal()` during `boot()` — typically when tokio's signal driver is unavailable or the process hit a resource/signal limit.

Common situations: Calling daemon boot outside a tokio runtime with the signal driver enabled (e.g. `current_thread` runtime built without `rt` features or via block_on misuse); RLIMIT_SIGPENDING exhaustion from leaked signal handlers; unusual runtimes without full signal support.

Related errors


AI-assisted analysis of atuinsh/atuin@c0c717ab04 (2026-09-12). Data as JSON: /api/errors/c5b66df5648797d3. Report an issue: GitHub.

Appendix: source

Thrown at crates/atuin-daemon/src/lib.rs:135

        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 c0c717ab04)