tokio-rs/tokio · error · io::Error
Refusing to register signal {signal}
Error message
Refusing to register signal {signal} What it means
signal_enable refuses to register a signal number that is <= 0 or is in signal_hook_registry::FORBIDDEN. These signals cannot be safely handled through the generic registry path (e.g. SIGKILL=9, SIGSTOP=19 on Linux cannot be caught). The error is returned as io::Error kind Other.
Source
Thrown at tokio/src/signal/unix.rs:272
/// Those two operations should both be async-signal safe.
fn action(globals: &'static Globals, signal: libc::c_int) {
globals.record_event(signal as EventId);
// Send a wakeup, ignore any errors (anything reasonably possible is
// full pipe and then it will wake up anyway).
let mut sender = &globals.sender;
drop(sender.write(&[1]));
}
/// Enables this module to receive signal notifications for the `signal`
/// provided.
///
/// This will register the signal handler if it hasn't already been registered,
/// returning any error along the way if that fails.
fn signal_enable(signal: SignalKind, handle: &Handle) -> io::Result<()> {
let signal = signal.0;
if signal <= 0 || signal_hook_registry::FORBIDDEN.contains(&signal) {
return Err(Error::new(
ErrorKind::Other,
format!("Refusing to register signal {signal}"),
));
}
// Check that we have a signal driver running
handle.check_inner()?;
let globals = globals();
let siginfo = match globals.storage().get(signal as EventId) {
Some(slot) => slot,
None => return Err(io::Error::new(io::ErrorKind::Other, "signal too large")),
};
siginfo
.init
.get_or_init(|| {
unsafe { signal_hook_registry::register(signal, move || action(globals, signal)) }View on GitHub (pinned to 625954f365)
Solutions
- Use a valid catchable signal: SignalKind::interrupt, SignalKind::terminate, SignalKind::hangup, etc.
- Validate the raw number is > 0 and not SIGKILL/SIGSTOP before passing it.
- Read signal_hook_registry::FORBIDDEN at startup to confirm your target signal is allowed.
- Never attempt to handle SIGKILL/SIGSTOP — design shutdown around SIGTERM/SIGINT instead.
Example fix
// before let s = signal(SignalKind::from_raw(9))?; // SIGKILL refused // after let s = signal(SignalKind::terminate())?; // SIGTERM
Defensive patterns
Strategy: validation
Validate before calling
const FORBIDDEN: &[i32] = &[9 /*SIGKILL*/, 19 /*SIGSTOP*/];
fn can_register(sig: i32) -> bool {
sig > 0 && !FORBIDDEN.contains(&sig)
}
if !can_register(num) { return Err(io::Error::new(io::ErrorKind::Other, "refused")); }
signal(SignalKind::from_raw(num))? Type guard
fn is_catchable_signal(sig: i32) -> bool {
sig > 0 && sig != 9 && sig != 19
} Try / catch
match signal(SignalKind::from_raw(num)) {
Ok(s) => s,
Err(e) if e.to_string().contains("Refusing to register") => return Err(e),
Err(e) => return Err(e),
} Prevention
- Prefer named SignalKind helpers (interrupt, terminate, hangup).
- Never attempt to handle SIGKILL/SIGSTOP.
- Validate raw signal numbers > 0 before registering.
When it happens
Trigger: Calling unix::signal(SignalKind::from_raw(n)) with n <= 0 or a forbidden value such as SIGKILL or SIGSTOP.
Common situations: Passing SignalKind::from_raw(0); trying to listen for SIGKILL/SIGSTOP; computing a signal number incorrectly and producing a non-positive value.
Related errors
- signal too large
- registering signal handler failed
- signal driver gone
- there is no signal driver running, must be called from the c
- not a pipe
AI-assisted analysis of tokio-rs/tokio@625954f365 (2026-08-11).
Data as JSON: /api/errors/902d129577b4b203.
Report an issue: GitHub.