{"record":{"id":"72a58cc0f73c298e","repo":"block/buzz","slug":"install-sigterm-handler","errorCode":null,"errorMessage":"install SIGTERM handler","messagePattern":"install SIGTERM handler","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/buzz-relay/src/main.rs","lineNumber":1528,"sourceCode":"    .with_graceful_shutdown(async move {\n        tcp_rx.changed().await.ok();\n    })\n    .await\n    .map_err(|e| anyhow::anyhow!(\"Server error: {e}\"))?;\n\n    let hard_shutdown = shutdown_handle\n        .await\n        .map_err(|e| anyhow::anyhow!(\"Shutdown task failed: {e}\"))?;\n    hard_shutdown.abort();\n    Ok(())\n}\n\n/// Wait for SIGTERM (Unix) or Ctrl+C.\nasync fn shutdown_signal() {\n    #[cfg(unix)]\n    {\n        use tokio::signal::unix::{signal, SignalKind};\n        let mut sigterm = signal(SignalKind::terminate()).expect(\"install SIGTERM handler\");\n        tokio::select! {\n            _ = tokio::signal::ctrl_c() => {},\n            _ = sigterm.recv() => {},\n        }\n    }\n    #[cfg(not(unix))]\n    {\n        tokio::signal::ctrl_c().await.ok();\n    }\n}\n/// Reconstruct a `nostr::Event` from a [`DueReminder`] row for Redis pub/sub.\nfn reminder_to_event(reminder: &buzz_db::event::DueReminder) -> nostr::Event {\n    let event_json = serde_json::json!({\n        \"id\": hex::encode(&reminder.id),\n        \"pubkey\": hex::encode(&reminder.pubkey),\n        \"created_at\": reminder.created_at.timestamp(),\n        \"kind\": reminder.kind as u16,\n        \"tags\": reminder.tags,","sourceCodeStart":1510,"sourceCodeEnd":1546,"githubUrl":"https://github.com/block/buzz/blob/dad5a33865fc81a2e55b3b60746632f615ec1e3a/crates/buzz-relay/src/main.rs#L1510-L1546","documentation":"shutdown_signal registers a Unix SIGTERM handler via tokio::signal::unix::signal, which calls sigaction under the hood. Registration returns io::Error when the OS or sandbox refuses to install the handler; the expect converts that to a panic, so the relay loses graceful shutdown (and the shutdown task panics the moment shutdown_signal is polled).","triggerScenarios":"Running the relay under a seccomp/container profile that blocks rt_sigaction for SIGTERM (gVisor, Firecracker, custom AppArmor/seccomp); environments without Unix signal support; rare resource exhaustion at handler registration.","commonSituations":"Over-restricted Docker/K8s seccomp profiles; embedding buzz-relay's run path in sandboxes or custom supervisors that already claim SIGTERM; hardened deployment images.","solutions":["Run with a seccomp profile that permits signal-handler installation (Docker's default unconfined-ish profile does)","Verify with strace that rt_sigaction for SIGTERM (15) succeeds in the target environment","If embedding the relay where SIGTERM cannot be handled, change the expect to error propagation with a fallback to ctrl_c-only waiting"],"exampleFix":"// before\nlet mut sigterm = signal(SignalKind::terminate()).expect(\"install SIGTERM handler\");\ntokio::select! {\n    _ = tokio::signal::ctrl_c() => {},\n    _ = sigterm.recv() => {},\n}\n\n// after\nlet mut sigterm = signal(SignalKind::terminate())\n    .map_err(|e| tracing::warn!(\"SIGTERM handler unavailable: {e}; ctrl_c only\"))\n    .ok();\ntokio::select! {\n    _ = tokio::signal::ctrl_c() => {},\n    _ = async { if let Some(s) = sigterm.as_mut() { s.recv().await; } } => {},\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"let sigterm = match signal(SignalKind::terminate()) {\n    Ok(s) => Some(s),\n    Err(e) => {\n        tracing::warn!(\"SIGTERM handler unavailable: {e}; falling back to ctrl_c\");\n        None\n    }\n};\ntokio::select! {\n    _ = tokio::signal::ctrl_c() => {},\n    _ = async { if let Some(mut s) = sigterm { s.recv().await; } } => {},\n}","preventionTips":["Smoke-test signal handling in the actual deployment sandbox, not just on a dev laptop","Keep container seccomp profiles permissive for rt_sigaction or run with the runtime default profile","For supervisors that manage SIGTERM themselves (systemd, k8s), verify the contract: they send SIGTERM and expect the app to exit, so the handler must be installable"],"tags":["rust","tokio","unix-signals","sigterm","shutdown","sandbox"],"backgroundTag":"signal-handler-install-failed","analyzedSha":"dad5a33865fc81a2e55b3b60746632f615ec1e3a","analyzedAt":"2026-08-20T04:38:24.874Z","contentChangedAt":"2026-08-20T04:38:24.874Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}