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

  1. Run with a seccomp profile that permits signal-handler installation (Docker's default unconfined-ish profile does)
  2. Verify with strace that rt_sigaction for SIGTERM (15) succeeds in the target environment
  3. 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

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


AI-assisted analysis of block/buzz@dad5a33865 (2026-08-20). Data as JSON: /api/errors/72a58cc0f73c298e. Report an issue: GitHub.