{"record":{"id":"8a66c2b6d8b3b9f3","repo":"linera-io/linera-protocol","slug":"failed-to-set-up-sigint-handler","errorCode":null,"errorMessage":"Failed to set up SIGINT handler","messagePattern":"Failed to set up SIGINT handler","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-base/src/lib.rs","lineNumber":180,"sourceCode":"    }\n    write!(f, \"]\")\n}\n\n/// Helper function for allocative.\npub fn visit_allocative_simple<T>(_: &T, visitor: &mut allocative::Visitor<'_>) {\n    visitor.visit_simple_sized::<T>();\n}\n\n/// Listens for shutdown signals, and notifies the [`CancellationToken`] if one is\n/// received.\n#[cfg(not(target_arch = \"wasm32\"))]\npub async fn listen_for_shutdown_signals(shutdown_sender: CancellationToken) {\n    let _shutdown_guard = shutdown_sender.drop_guard();\n\n    #[cfg(unix)]\n    {\n        let mut sigint =\n            unix::signal(unix::SignalKind::interrupt()).expect(\"Failed to set up SIGINT handler\");\n        let mut sigterm =\n            unix::signal(unix::SignalKind::terminate()).expect(\"Failed to set up SIGTERM handler\");\n        let mut sighup =\n            unix::signal(unix::SignalKind::hangup()).expect(\"Failed to set up SIGHUP handler\");\n\n        tokio::select! {\n            _ = sigint.recv() => debug!(\"Received SIGINT\"),\n            _ = sigterm.recv() => debug!(\"Received SIGTERM\"),\n            _ = sighup.recv() => debug!(\"Received SIGHUP\"),\n        }\n    }\n\n    #[cfg(windows)]\n    {\n        tokio::signal::ctrl_c()\n            .await\n            .expect(\"Failed to set up Ctrl+C handler\");\n        debug!(\"Received Ctrl+C\");","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-base/src/lib.rs#L162-L198","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlet rt = tokio::runtime::Builder::new_multi_thread()\n    .worker_threads(4)\n    .build()?;\nrt.spawn(linera_base::listen_for_shutdown_signals(token));\n\n// after — the signal driver must be enabled before signal() can register\nlet rt = tokio::runtime::Builder::new_multi_thread()\n    .worker_threads(4)\n    .enable_all() // registers the IO + signal drivers\n    .build()?;\nrt.spawn(linera_base::listen_for_shutdown_signals(token));","handlingStrategy":"validation","validationCode":"// Before spawning the listener, guarantee the runtime has the signal driver:\nlet rt = tokio::runtime::Builder::new_multi_thread()\n    .enable_all() // required for unix::signal() registration\n    .build()?;\nrt.spawn(linera_base::listen_for_shutdown_signals(token));","typeGuard":null,"tryCatchPattern":"let handle = tokio::spawn(listen_for_shutdown_signals(token));\nif let Err(join_err) = handle.await {\n    if join_err.is_panic() {\n        // drop_guard already cancelled the token: begin your own shutdown.\n        eprintln!(\"signal listener failed: {join_err}\");\n        shutdown_now().await;\n    }\n}","preventionTips":["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"],"tags":["rust","tokio","signals","unix","sigint","startup","panic"],"backgroundTag":"signal-handler-registration-failed","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}