imsnif/bandwhich · error

failed to set SIGINT handler

Error message

failed to set SIGINT handler

What it means

At this point in start(), bandwhich registers a SIGINT handler (via the signal_hook crate) so that Ctrl+C terminates the process cleanly instead of being consumed as a regular keypress by the terminal event loop (per issue #487). This error fires when the underlying OS-level signal registration call fails — e.g. signal_hook's register/flag returns an Err, which happens on OS errors such as reaching the per-process limit of registered signal handlers, or unsupported/broken signal APIs on the platform. Since clean shutdown on Ctrl+C is mandatory, start() aborts startup with this message rather than running a session that cannot be interrupted gracefully.

Solutions

  1. Check and raise the file descriptor limit (`ulimit -n`) if fds are exhausted
  2. Retry in a normal environment — this is usually environmental, not a code bug
  3. If signal handling cannot work in your sandbox, run bandwhich directly in a standard shell/terminal
  4. Upgrade bandwhich/ctrlc if on an old version with known platform issues

Example fix

// before
bandwhich  # in sandbox with fd exhaustion: panicked at 'failed to set SIGINT handler'
// after
ulimit -n 4096 && bandwhich
Defensive patterns

Strategy: try-catch

Validate before calling

// shell: preflight fd availability
current=$(ulimit -n)
if [ "$current" -lt 256 ]; then ulimit -n 4096; fi

Try / catch

if let Err(e) = ctrlc::set_handler(move || { running.store(false, Ordering::Release); }) {
    eprintln!("SIGINT handler unavailable: {e}; Ctrl+C will kill abruptly");
}

Prevention

When it happens

Trigger: `ctrlc::set_handler` returning Err — typically when creating the signal pipe/handler fails, e.g. resource exhaustion (too many file descriptors, no signal dispositions available) or unsupported/locked-down environments.

Common situations: Containers or sandboxed environments with restricted signal handling or fd limits (ulimit -n exhausted), heavily constrained CI runners, or unusual platforms where the ctrlc crate's mechanism (eventfd/signal) is unavailable.

Understand the failure class

Background: "unsupported platform" / "not supported on this platform" errors: what they mean and how to fix them — this error's family across 47 libraries.

Related errors


AI-assisted analysis of imsnif/bandwhich@1899870cea (2026-09-08). Data as JSON: /api/errors/3b92ff381ead8b4e. Report an issue: GitHub.

Appendix: source

Thrown at src/main.rs:112

pub fn start<B>(terminal_backend: B, os_input: OsInputOutput, opts: Opt)
where
    B: Backend + Send + 'static,
{
    let running = Arc::new(AtomicBool::new(true));
    let paused = Arc::new(AtomicBool::new(false));
    let last_start_time = Arc::new(RwLock::new(Instant::now()));
    let cumulative_time = Arc::new(RwLock::new(Duration::new(0, 0)));
    let table_cycle_offset = Arc::new(AtomicUsize::new(0));

    // handle SIGINT properly instead of as a keypress
    // see https://github.com/imsnif/bandwhich/issues/487
    #[cfg(not(test))]
    {
        let running = running.clone();
        ctrlc::set_handler(move || {
            running.store(false, Ordering::Release);
        })
        .expect("failed to set SIGINT handler");
    }

    let mut active_threads = vec![];

    let terminal_events = os_input.terminal_events;
    let get_open_sockets = os_input.get_open_sockets;
    let mut write_to_stdout = os_input.write_to_stdout;
    let mut dns_client = os_input.dns_client;

    let raw_mode = opts.raw;

    let network_utilization = Arc::new(Mutex::new(Utilization::new()));
    let ui = Arc::new(Mutex::new(Ui::new(terminal_backend, &opts)));

    let display_handler = thread::Builder::new()
        .name("display_handler".to_string())
        .spawn({
            let running = running.clone();

View on GitHub (pinned to 1899870cea)