tokio-rs/tokio · error · io::Error
registering signal handler failed
Error message
registering signal handler failed
What it means
signal_enable, in its init closure, calls signal_hook_registry::register and on failure maps the error to 'registering signal handler failed' (ErrorKind::Other) when raw_os_error is unavailable, or to the raw OS error otherwise. It means the OS-level sigaction registration failed.
Source
Thrown at tokio/src/signal/unix.rs:296
// 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)) }
.map(|_| ())
.map_err(|e| e.raw_os_error())
})
.map_err(|e| {
e.map_or_else(
|| Error::new(ErrorKind::Other, "registering signal handler failed"),
Error::from_raw_os_error,
)
})
}
/// An listener for receiving a particular type of OS signal.
///
/// The listener can be turned into a `Stream` using [`SignalStream`].
///
/// [`SignalStream`]: https://docs.rs/tokio-stream/latest/tokio_stream/wrappers/struct.SignalStream.html
///
/// In general signal handling on Unix is a pretty tricky topic, and this
/// structure is no exception! There are some important limitations to keep in
/// mind when using `Signal` streams:
///
/// * Signals handling in Unix already necessitates coalescing signals
/// together sometimes. This `Signal` stream is also no exception here in
/// that it will also coalesce signals. That is, even if the signal handlerView on GitHub (pinned to 625954f365)
Solutions
- Inspect the underlying raw_os_error if present (check e.raw_os_error()) for the OS errno.
- Ensure only one framework (tokio) manages a given signal — remove manual libc::signal/libc::sigaction calls.
- Register signals early at startup before any conflicting libraries do.
- Update signal-hook / signal-hook-registry to a compatible version.
Example fix
// before
let s = signal(kind)?; // 'registering signal handler failed'
// after
let s = match signal(kind) {
Ok(s) => s,
Err(e) => {
eprintln!("errno={:?} kind={:?}", e.raw_os_error(), e.kind());
return Err(e);
}
}; Defensive patterns
Strategy: try-catch
Validate before calling
// Register signals at startup before any other crate can claim them. // Ensure no other framework calls libc::signal/sigaction for the same signum.
Try / catch
match signal(kind) {
Ok(s) => s,
Err(e) => {
eprintln!("signal register failed: errno={:?} kind={:?}", e.raw_os_error(), e.kind());
return Err(e);
}
} Prevention
- Register all signal handlers at process startup.
- Avoid mixing tokio signal handling with manual libc sigaction for the same signal.
- Keep signal-hook / signal-hook-registry versions consistent with tokio.
When it happens
Trigger: Registering a signal handler fails at the OS level: invalid signal, permission denied, or signal_hook_registry internals reject it; rare race during handler setup.
Common situations: Conflicting signal handlers set by a C library or another crate; calling signal() outside tokio that interferes; resource/threading limits during handler thread spawn; buggy platform ABI.
Related errors
- Refusing to register signal {signal}
- signal too large
- 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/06a38c15c11d076b.
Report an issue: GitHub.