{"record":{"id":"2cca0f5714be4396","repo":"linera-io/linera-protocol","slug":"failed-to-set-up-sigterm-handler","errorCode":null,"errorMessage":"Failed to set up SIGTERM handler","messagePattern":"Failed to set up SIGTERM handler","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-base/src/lib.rs","lineNumber":182,"sourceCode":"}\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\");\n    }\n}","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-base/src/lib.rs#L164-L200","documentation":"Panics when tokio::signal::unix::signal(SignalKind::terminate()) cannot install a SIGTERM handler in listen_for_shutdown_signals. The registration itself fails at startup, before any signal is delivered — typically because the tokio runtime lacks the signal/IO driver or the OS refused to install the handler. The panic drops the shutdown guard, so the CancellationToken fires and the node shuts down immediately.","triggerScenarios":"Running the node's signal listener on a runtime built without .enable_all(); a sandboxed or restricted environment (seccomp, gVisor, some minimal containers) where sigaction for SIGTERM is denied; resource exhaustion (EMFILE) preventing tokio's global signal registry from being created; tokio compiled without the 'signal' feature.","commonSituations":"Custom binaries or integration tests that hand-construct a tokio Runtime and call linera-base's run(); deploying validators into hardened container images; CI environments that strip tokio default features via a shared workspace Cargo.toml.","solutions":["Enable the full driver set on the runtime: .enable_all() (or .enable_io() plus the signal feature) before spawning the listener","Confirm tokio's 'signal' feature is present: cargo tree -e features -i tokio","Audit the container/sandbox security profile for sigaction restrictions on SIGTERM","Raise file-descriptor limits if startup also logs EMFILE/ENOBUFS errors","Treat a panicked listener task as fatal — the shutdown token was already cancelled, so perform a clean exit"],"exampleFix":"// before\nfn main() {\n    let rt = tokio::runtime::Builder::new_current_thread().build().unwrap();\n    rt.block_on(async { /* ... spawn listen_for_shutdown_signals ... */ });\n}\n\n// after\nfn main() {\n    let rt = tokio::runtime::Builder::new_current_thread()\n        .enable_all() // without this, unix::signal(SignalKind::terminate()) fails\n        .build()\n        .unwrap();\n    rt.block_on(async { /* ... */ });\n}","handlingStrategy":"validation","validationCode":"// Signal registration needs the runtime's IO/signal driver:\nlet rt = tokio::runtime::Builder::new_multi_thread()\n    .enable_all()\n    .build()?;\n// Only now is listen_for_shutdown_signals safe to spawn for SIGTERM.","typeGuard":null,"tryCatchPattern":"let handle = tokio::spawn(listen_for_shutdown_signals(token));\nif let Err(e) = handle.await {\n    assert!(!e.is_panic(), \"signal listener panicked; token already cancelled — exit now\");\n}","preventionTips":["Prefer #[tokio::main] (which enables all drivers) over manual Runtime::new variants","Keep supervisors (systemd) sending SIGTERM only to the main PID, not to a session with conflicting handlers","Document in your runbook that a panic here means the process is already shutting down via the token"],"tags":["rust","tokio","signals","unix","sigterm","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"}