bootandy/dust · error

Error setting Ctrl-C handler

Error message

Error setting Ctrl-C handler

What it means

This panic comes from `.expect()` on the `Result` returned by `ctrlc::set_handler`, which registers a SIGINT (Ctrl-C) handler for the process. The ctrlc crate throws this error when it fails to install the OS-level signal handler — typically because the underlying OS API (signal/sigaction on Unix, SetConsoleCtrlHandler on Windows) failed, or because the internal wakeup pipe/socket could not be created.

Solutions

  1. Check the environment the program runs in: remove seccomp/sandbox syscall restrictions blocking pipe2 or signal installation, and raise the file-descriptor limit (ulimit -n).
  2. Call `ctrlc::set_handler` and handle the Err case gracefully instead of panicking, so the tool still runs (just without Ctrl-C handling): `if let Err(e) = ctrlc::set_handler(...) { eprintln!("warning: Ctrl-C handling unavailable: {e}"); }`.
  3. Ensure ctrlc runs only once per process and before threads that matter are spawned; a second set_handler call or a mis-sequenced init can fail.
  4. Update the ctrlc crate to the latest version; older versions had platform-specific handler-installation bugs.
  5. If the environment genuinely cannot support signal handlers (e.g. some embedded/WASI targets), drop the ctrlc dependency or gate it behind a feature flag / target check.

Example fix

// before
ctrlc::set_handler(move || {
    println!("\nAborting");
    process::exit(1);
})
.expect("Error setting Ctrl-C handler");
// after
if let Err(e) = ctrlc::set_handler(move || {
    println!("\nAborting");
    process::exit(1);
}) {
    eprintln!("warning: could not install Ctrl-C handler: {e}");
}
Defensive patterns

Strategy: fallback

Validate before calling

// No portable pre-check exists; detect the environment that breaks ctrlc:
// e.g. check fd availability before startup
match std::fs::File::create("/dev/null") {
    Ok(_) => {},
    Err(e) => eprintln!("fd exhaustion likely ({e}); signal handler may fail to install"),
}

Type guard

fn ctrlc_supported() -> bool {
    #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
    { true }
    #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
    { false }
}

Try / catch

match ctrlc::set_handler(|| {
    eprintln!("\nAborting");
    std::process::exit(1);
}) {
    Ok(()) => {}
    Err(e) => eprintln!("warning: Ctrl-C handling unavailable: {e}"),
}

Prevention

When it happens

Trigger: Calling `ctrlc::set_handler` when: (1) the OS refuses to install a signal handler (e.g. signal/sigaction returns an error), (2) the crate cannot create its internal self-pipe (pipe/socketpair failure, often fd exhaustion or a hardened seccomp/sandbox blocking pipe2), (3) running in an environment without a proper signal mechanism (some containers, CI sandboxes, or processes with a masked/disabled signal mask), or (4) set_handler is invoked in a context where registering handlers is not permitted.

Common situations: Running the binary inside a restricted container or sandbox (Docker with restricted syscall filters, gVisor, seccomp profiles) that blocks pipe2/signal syscalls; processes with exhausted file descriptors so the ctrlc self-pipe cannot be created; embedding the program as a PID-1 process or in environments where signal handlers cannot be registered; uncommon but possible resource exhaustion at startup.


AI-assisted analysis of bootandy/dust@8a846f6689 (2026-09-08). Data as JSON: /api/errors/dd4f4d03db181177. Report an issue: GitHub.

Appendix: source

Thrown at src/main.rs:129

                process::exit(1)
            })
        })
        .collect()
}

fn main() {
    let options = Cli::parse();
    let config = get_config(options.config.as_ref());

    let errors = RuntimeErrors::default();
    let error_listen_for_ctrlc = Arc::new(Mutex::new(errors));
    let errors_for_rayon = error_listen_for_ctrlc.clone();

    ctrlc::set_handler(move || {
        println!("\nAborting");
        process::exit(1);
    })
    .expect("Error setting Ctrl-C handler");

    let target_dirs = if let Some(path) = config.get_files0_from(&options) {
        read_paths_from_source(&path, true)
    } else if let Some(path) = config.get_files_from(&options) {
        read_paths_from_source(&path, false)
    } else {
        match options.params {
            Some(ref values) => values.clone(),
            None => vec![".".to_owned()],
        }
    }
    .into_iter()
    .filter(|path| !path.is_empty())
    .collect::<Vec<_>>();

    let summarize_file_types = options.file_types;

    let filter_regexs = get_regex_value(options.filter.as_ref());

View on GitHub (pinned to 8a846f6689)