linera-io/linera-protocol · error
Failed to set up Ctrl+C handler
Error message
Failed to set up Ctrl+C handler
What it means
Panics when tokio::signal::ctrl_c().await returns an Err on Windows inside listen_for_shutdown_signals. ctrl_c() fails at registration time when SetConsoleCtrlHandler cannot install the handler — restricted environments, service contexts without a console, handler-table exhaustion, or a runtime without the IO/signal driver. The panic runs the drop_guard, cancelling the shutdown token and starting an immediate shutdown.
Source
Thrown at linera-base/src/lib.rs:197
let mut sigint =
unix::signal(unix::SignalKind::interrupt()).expect("Failed to set up SIGINT handler");
let mut sigterm =
unix::signal(unix::SignalKind::terminate()).expect("Failed to set up SIGTERM handler");
let mut sighup =
unix::signal(unix::SignalKind::hangup()).expect("Failed to set up SIGHUP handler");
tokio::select! {
_ = sigint.recv() => debug!("Received SIGINT"),
_ = sigterm.recv() => debug!("Received SIGTERM"),
_ = sighup.recv() => debug!("Received SIGHUP"),
}
}
#[cfg(windows)]
{
tokio::signal::ctrl_c()
.await
.expect("Failed to set up Ctrl+C handler");
debug!("Received Ctrl+C");
}
}
/// Registers every metric this crate declares.
///
/// Without this, a metric is only exported after the code path that observes it has run, so a
/// rarely-taken path leaves its panels blank and makes a routine restart look like the metric
/// was removed.
#[cfg(with_metrics)]
pub fn init_metrics() {
data_types::metrics::init_metrics();
panic_hook::metrics::init_metrics();
}
View on GitHub (pinned to 6c226ddcb3)
Solutions
- Build the tokio Runtime with .enable_all() before spawning the listener
- If running as a Windows service, handle service control events via the windows-service crate instead of relying on console Ctrl+C
- Remove competing console-handler registrations from other crates or preload DLLs
- Run in an interactive console session to confirm the handler installs, then re-enable the service context
- Treat a panicked listener task as fatal — the shutdown token was cancelled, so exit cleanly
Example fix
// before
let rt = tokio::runtime::Builder::new_multi_thread().build()?; // no enable_all
rt.spawn(linera_base::listen_for_shutdown_signals(token));
// after
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?;
rt.spawn(linera_base::listen_for_shutdown_signals(token));
// If running as a Windows service, additionally subscribe to service stop events
// instead of relying solely on Ctrl+C. Defensive patterns
Strategy: validation
Validate before calling
// On Windows, the runtime must have the IO driver for ctrl_c registration:
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?;
rt.spawn(linera_base::listen_for_shutdown_signals(token)); Try / catch
let handle = tokio::spawn(listen_for_shutdown_signals(token));
if let Err(e) = handle.await {
if e.is_panic() {
// ctrl_c handler registration failed; token cancelled via drop_guard.
log::error!("ctrl_c registration failed: {e}");
graceful_shutdown().await;
}
} Prevention
- Run the node in an interactive console during development to confirm Ctrl+C works before service deployment
- For Windows services, subscribe to service control stop events separately instead of relying on console Ctrl+C
- Limit the number of crates registering console handlers in the same process
When it happens
Trigger: Running the node as a Windows service or in a session without an interactive console; a runtime built without .enable_all() so the signal infrastructure is absent; a sandboxed Windows environment ( restricted AppContainer) denying SetConsoleCtrlHandler; too many console handlers already registered by other libraries.
Common situations: Windows CI agents running the node headless; embedding linera-base in a GUI application or service wrapper on Windows; multiple signal-handling crates (ctrlc, windows-service) competing for console-handler slots.
Related errors
- Failed to set up SIGINT handler
- Failed to set up SIGTERM handler
- Failed to set up SIGHUP handler
- owner should be different from spender
- invalid block export configuration: {message}
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/85610a9e98e138f5.
Report an issue: GitHub.