{"record":{"id":"114721ce59cb598e","repo":"linera-io/linera-protocol","slug":"failed-to-set-up-sighup-handler","errorCode":null,"errorMessage":"Failed to set up SIGHUP handler","messagePattern":"Failed to set up SIGHUP handler","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-base/src/lib.rs","lineNumber":184,"sourceCode":"/// 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\");\n    }\n}\n\n/// Registers every metric this crate declares.","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-base/src/lib.rs#L166-L202","documentation":"Panics when tokio::signal::unix::signal(SignalKind::hangup()) fails to install a SIGHUP handler in listen_for_shutdown_signals. Like the SIGINT/SIGTERM cases, this is a registration-time failure: the tokio runtime has no signal driver, the global signal registry could not be created, or the environment blocks handler installation. The drop_guard then cancels the CancellationToken, so the process starts an immediate shutdown.","triggerScenarios":"Running the signal listener on a runtime without .enable_all(); nested runtimes (spawning the listener inside Runtime::block_on of a runtime lacking the signal driver); sandboxes denying sigaction; EMFILE at process start. SITESPECIFIC: daemons that re-exec or fork (SIGHUP is the reload signal) sometimes install their own SIGHUP handler first, exhausting or conflicting with tokio's registry.","commonSituations":"Running the node as a systemd/init service where the supervisor or a wrapper script also manipulates SIGHUP; custom embedding of linera-base with a manually built runtime; minimal base images (distroless, scratch) with restricted syscall profiles.","solutions":["Build the tokio Runtime with .enable_all() so the signal driver is registered","Verify the 'signal' feature is enabled for tokio in the dependency graph","Remove pre-installed SIGHUP handlers (libc::signal, signal-hook) from wrapper code or preload libraries","Check the sandbox/seccomp profile and file-descriptor limits","Monitor the spawned task's JoinHandle and exit cleanly — the shutdown token has already been cancelled by the drop guard"],"exampleFix":"// before\nlet rt = tokio::runtime::Runtime::new().unwrap(); // no signal driver in some configs\nrt.spawn(listen_for_shutdown_signals(token));\n\n// after\n#[tokio::main] // #[tokio::main] and #[tokio::test] call enable_all() for you\nasync fn main() {\n    tokio::spawn(listen_for_shutdown_signals(token));\n    // ...\n}","handlingStrategy":"validation","validationCode":"// Ensure the runtime the listener runs on was built with the signal driver:\nlet rt = tokio::runtime::Builder::new_multi_thread()\n    .enable_all()\n    .build()?;\nrt.spawn(linera_base::listen_for_shutdown_signals(token));","typeGuard":null,"tryCatchPattern":"match tokio::spawn(listen_for_shutdown_signals(token)).await {\n    Ok(()) => {}\n    Err(e) if e.is_panic() => {\n        // SIGHUP registration failed and the token fired: treat as fatal,\n        // do not respawn in a loop.\n        std::process::exit(1);\n    }\n    Err(e) => panic!(\"listener task failed: {e}\"),\n}","preventionTips":["Do not install custom SIGHUP handlers in wrapper processes; let tokio own reload-style signals","When embedding the node, spawn signal listeners from the outermost runtime, not nested ones","Test daemon behavior with kill -HUP in an environment matching production"],"tags":["rust","tokio","signals","unix","sighup","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"}