block/buzz · critical
install SIGTERM handler
Error message
install SIGTERM handler
What it means
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).
Source
Thrown at crates/buzz-relay/src/main.rs:1528
.with_graceful_shutdown(async move {
tcp_rx.changed().await.ok();
})
.await
.map_err(|e| anyhow::anyhow!("Server error: {e}"))?;
let hard_shutdown = shutdown_handle
.await
.map_err(|e| anyhow::anyhow!("Shutdown task failed: {e}"))?;
hard_shutdown.abort();
Ok(())
}
/// Wait for SIGTERM (Unix) or Ctrl+C.
async fn shutdown_signal() {
#[cfg(unix)]
{
use tokio::signal::unix::{signal, SignalKind};
let mut sigterm = signal(SignalKind::terminate()).expect("install SIGTERM handler");
tokio::select! {
_ = tokio::signal::ctrl_c() => {},
_ = sigterm.recv() => {},
}
}
#[cfg(not(unix))]
{
tokio::signal::ctrl_c().await.ok();
}
}
/// Reconstruct a `nostr::Event` from a [`DueReminder`] row for Redis pub/sub.
fn reminder_to_event(reminder: &buzz_db::event::DueReminder) -> nostr::Event {
let event_json = serde_json::json!({
"id": hex::encode(&reminder.id),
"pubkey": hex::encode(&reminder.pubkey),
"created_at": reminder.created_at.timestamp(),
"kind": reminder.kind as u16,
"tags": reminder.tags,View on GitHub (pinned to dad5a33865)
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
Example fix
// before
let mut sigterm = signal(SignalKind::terminate()).expect("install SIGTERM handler");
tokio::select! {
_ = tokio::signal::ctrl_c() => {},
_ = sigterm.recv() => {},
}
// after
let mut sigterm = signal(SignalKind::terminate())
.map_err(|e| tracing::warn!("SIGTERM handler unavailable: {e}; ctrl_c only"))
.ok();
tokio::select! {
_ = tokio::signal::ctrl_c() => {},
_ = async { if let Some(s) = sigterm.as_mut() { s.recv().await; } } => {},
} Defensive patterns
Strategy: fallback
Try / catch
let sigterm = match signal(SignalKind::terminate()) {
Ok(s) => Some(s),
Err(e) => {
tracing::warn!("SIGTERM handler unavailable: {e}; falling back to ctrl_c");
None
}
};
tokio::select! {
_ = tokio::signal::ctrl_c() => {},
_ = async { if let Some(mut s) = sigterm { s.recv().await; } } => {},
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Over-restricted Docker/K8s seccomp profiles; embedding buzz-relay's run path in sandboxes or custom supervisors that already claim SIGTERM; hardened deployment images.
Related errors
- Shutdown task failed: {e}
- pool initialization cancelled by shutdown
- failed to install rustls crypto provider
- connect to test DB
- push HTTP client
AI-assisted analysis of block/buzz@dad5a33865 (2026-08-20).
Data as JSON: /api/errors/72a58cc0f73c298e.
Report an issue: GitHub.