vectordotdev/vector · error

Failed to set up SIGINT handler.

Error message

Failed to set up SIGINT handler.

What it means

tokio's signal(SignalKind::interrupt()) registers a Unix signal handler and returns io::Result; Vector registers SIGINT inside os_signals() and unwraps with expect. If registration fails (signal syscalls blocked by the sandbox, handler/resource limits hit, or too many registrations in one process), Vector panics during startup. The four sibling expects cover SIGINT, SIGTERM, SIGQUIT and SIGHUP.

Source

Thrown at src/signal.rs:196

    }

    /// Shutdown active signal handlers.
    pub fn clear(&mut self) {
        for shutdown_tx in self.shutdown_txs.drain(..) {
            // An error just means the channel was already shut down; safe to ignore.
            _ = shutdown_tx.send(());
        }
    }
}

/// Signals from OS/user.
#[cfg(unix)]
fn os_signals(runtime: &Runtime) -> impl Stream<Item = SignalTo> + use<> {
    use tokio::signal::unix::{SignalKind, signal};

    // The `signal` function must be run within the context of a Tokio runtime.
    runtime.block_on(async {
        let mut sigint = signal(SignalKind::interrupt()).expect("Failed to set up SIGINT handler.");
        let mut sigterm =
            signal(SignalKind::terminate()).expect("Failed to set up SIGTERM handler.");
        let mut sigquit = signal(SignalKind::quit()).expect("Failed to set up SIGQUIT handler.");
        let mut sighup = signal(SignalKind::hangup()).expect("Failed to set up SIGHUP handler.");

        async_stream::stream! {
            loop {
                let signal = tokio::select! {
                    _ = sigint.recv() => {
                        info!(message = "Signal received.", signal = "SIGINT");
                        SignalTo::Shutdown(None)
                    },
                    _ = sigterm.recv() => {
                        info!(message = "Signal received.", signal = "SIGTERM");
                        SignalTo::Shutdown(None)
                    } ,
                    _ = sigquit.recv() => {
                        info!(message = "Signal received.", signal = "SIGQUIT");

View on GitHub (pinned to 3708c39b12)

Solutions

  1. Run with the default container seccomp profile or allow signal-related syscalls (rt_sigaction, signalfd) in the custom profile
  2. Raise the file-descriptor limit (ulimit -n 65536, or LimitNOFILE= in systemd) if fd exhaustion is the cause
  3. If supervising Vector, prefer its API or a clean exec-based handoff instead of installing conflicting handlers in the same process
  4. Reproduce on the host outside the sandbox to confirm the environment, not the config, is at fault

Example fix

# before: custom seccomp blocks signal syscalls, Vector panics at boot
docker run --security-opt seccomp=strict.json vector:latest

# after: default profile (or unconfined for diagnosis)
docker run vector:latest
# and raise fd limits where needed:
#   ulimit -n 65536
Defensive patterns

Strategy: validation

Validate before calling

# fail fast before launching Vector
[ "$(ulimit -n)" -ge 4096 ] || ulimit -n 4096
vector validate /etc/vector/vector.toml && exec vector --config /etc/vector/vector.toml

Prevention

When it happens

Trigger: Starting Vector in an environment where signal(2)/signalfd registration fails for SIGINT: a container seccomp/apparmor profile blocking signal syscalls, exhaustion of the fd/handler budget by many embedded runtimes (test harnesses), or a platform without the signal kind.

Common situations: Overly restrictive Docker/Kubernetes seccomp profiles; custom supervisors chaining signal handling; test harnesses that repeatedly create Tokio runtimes registering handlers; very low RLIMIT_NOFILE.

Related errors


AI-assisted analysis of vectordotdev/vector@3708c39b12 (2026-08-20). Data as JSON: /api/errors/1be83887958b2f43. Report an issue: GitHub.