linera-io/linera-protocol · error
Failed to set up SIGINT handler
Error message
Failed to set up SIGINT handler
What it means
Panics when tokio::signal::unix::signal(SignalKind::interrupt()) fails to install a SIGINT handler inside listen_for_shutdown_signals, the task Linera spawns to translate OS signals into CancellationToken cancellation. Tokio returns an error when the runtime was built without the signal/IO driver, when a global handler could not be registered (handler table full, resource limits, sandbox restrictions), or when the environment blocks sigaction. Because the panic fires while the drop_guard is alive, the shutdown token is cancelled and the process immediately begins shutting down.
Source
Thrown at linera-base/src/lib.rs:180
}
write!(f, "]")
}
/// Helper function for allocative.
pub fn visit_allocative_simple<T>(_: &T, visitor: &mut allocative::Visitor<'_>) {
visitor.visit_simple_sized::<T>();
}
/// Listens for shutdown signals, and notifies the [`CancellationToken`] if one is
/// received.
#[cfg(not(target_arch = "wasm32"))]
pub async fn listen_for_shutdown_signals(shutdown_sender: CancellationToken) {
let _shutdown_guard = shutdown_sender.drop_guard();
#[cfg(unix)]
{
let mut sigint =
unix::signal(unix::SignalKind::interrupt()).expect("Failed to set up SIGINT handler");
let mut sigterm =
unix::signal(unix::SignalKind::terminate()).expect("Failed to set up SIGTERM handler");
let mut sighup =
unix::signal(unix::SignalKind::hangup()).expect("Failed to set up SIGHUP handler");
tokio::select! {
_ = sigint.recv() => debug!("Received SIGINT"),
_ = sigterm.recv() => debug!("Received SIGTERM"),
_ = sighup.recv() => debug!("Received SIGHUP"),
}
}
#[cfg(windows)]
{
tokio::signal::ctrl_c()
.await
.expect("Failed to set up Ctrl+C handler");
debug!("Received Ctrl+C");View on GitHub (pinned to 6c226ddcb3)
Solutions
- Build the runtime with the signal driver enabled: tokio::runtime::Builder::new_multi_thread().enable_all().build()
- Verify tokio is compiled with the 'signal' feature in your dependency graph (cargo tree -e features -i tokio)
- Remove any raw libc::signal / signal-hook handlers for SIGINT that conflict with tokio's global registry
- Check file-descriptor limits (ulimit -n) and sandbox policy if registration still fails
- If you spawn the task yourself, monitor its JoinHandle and treat a panic as a fatal startup error rather than respawning blindly
Example fix
// before
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(4)
.build()?;
rt.spawn(linera_base::listen_for_shutdown_signals(token));
// after — the signal driver must be enabled before signal() can register
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(4)
.enable_all() // registers the IO + signal drivers
.build()?;
rt.spawn(linera_base::listen_for_shutdown_signals(token)); Defensive patterns
Strategy: validation
Validate before calling
// Before spawning the listener, guarantee the runtime has the signal driver:
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all() // required for unix::signal() registration
.build()?;
rt.spawn(linera_base::listen_for_shutdown_signals(token)); Try / catch
let handle = tokio::spawn(listen_for_shutdown_signals(token));
if let Err(join_err) = handle.await {
if join_err.is_panic() {
// drop_guard already cancelled the token: begin your own shutdown.
eprintln!("signal listener failed: {join_err}");
shutdown_now().await;
}
} Prevention
- Never hand-build a tokio Runtime without .enable_all() when hosting code that registers signals
- Check cargo tree -e features -i tokio for the 'signal' feature after dependency upgrades
- Avoid installing libc-level handlers for SIGINT before tokio initialization
- Smoke-test shutdown with kill -INT <pid> in a staging environment before shipping
When it happens
Trigger: Calling run/handle_net_up_service on a hand-built tokio Runtime lacking .enable_all()/.enable_io(); running the node under a seccomp/gVisor sandbox that restricts signal-handler installation; hitting EMFILE at startup so the signal pipe cannot be created; compiling tokio without the 'signal' feature (default-features = false) so unix::signal registration fails.
Common situations: Embedding linera-base's run loop in a custom binary or test that constructs its own tokio Runtime; running validators in hardened containers; tokio upgrades where feature unification silently drops the signal feature; spawning the listener from inside another runtime's block_on.
Related errors
- Failed to set up SIGTERM handler
- Failed to set up SIGHUP handler
- Failed to set up Ctrl+C handler
- owner should be different from spender
- invalid block export configuration: {message}
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/8a66c2b6d8b3b9f3.
Report an issue: GitHub.