sigoden/aichat · error · Panic
Failed to install CTRL+C signal handler
Error message
Failed to install CTRL+C signal handler
What it means
The server's graceful-shutdown helper calls tokio::signal::ctrl_c() and unwraps with this message; the OS refused to install the SIGINT handler. tokio returns an error here only when the signal cannot be registered with the runtime/OS — essentially never in normal operation.
Solutions
- Enable the tokio "signal" feature in Cargo.toml (tokio = { version = "1", features = ["signal", "full"] }).
- Run the server inside a normal #[tokio::main] runtime so the signal driver is available.
- If the platform cannot register SIGINT, replace the .expect with graceful degradation: log and fall back to terminating without a handler.
Example fix
// before
async fn shutdown_signal() {
tokio::signal::ctrl_c().await.expect("Failed to install CTRL+C signal handler")
}
// after
async fn shutdown_signal() {
if tokio::signal::ctrl_c().await.is_err() {
eprintln!("SIGINT handler unavailable; press Ctrl+C to kill");
std::future::pending::<()>().await;
}
} Defensive patterns
Strategy: try-catch
Try / catch
match tokio::signal::ctrl_c().await {
Ok(()) => tracing::info!("shutting down on SIGINT"),
Err(e) => tracing::warn!("ctrl_c handler unavailable: {e}"),
} Prevention
- Enable tokio's "signal" feature and run inside a standard tokio runtime
- Never unwrap signal installation in code that may run on restricted platforms
- Test the binary in your actual container/seccomp environment
When it happens
Trigger: Running outside a tokio runtime context where the signal driver is unavailable; operating systems or sandboxes that block SIGINT registration (some restricted containers, exotic platforms); tokio runtime built without the "signal" feature.
Common situations: Embedding the serve binary inside another runtime or test harness without enabling tokio's signal feature; minimal container images with restricted seccomp profiles; custom tokio runtime configurations.
AI-assisted analysis of sigoden/aichat@82976d349a (2026-09-09).
Data as JSON: /api/errors/aa9bba9941d9a2ff.
Report an issue: GitHub.
Appendix: source
Thrown at src/serve.rs:629
struct RerankReqBody {
documents: Vec<String>,
query: String,
model: String,
top_n: Option<usize>,
}
#[derive(Debug)]
enum ResEvent {
First(Option<String>),
Text(String),
ToolCalls(Vec<ToolCall>),
Done,
}
async fn shutdown_signal() {
tokio::signal::ctrl_c()
.await
.expect("Failed to install CTRL+C signal handler")
}
fn generate_completion_id() -> String {
let random_id = chrono::Utc::now().nanosecond();
format!("chatcmpl-{random_id}")
}
fn set_cors_header(res: &mut AppResponse) {
res.headers_mut().insert(
hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN,
hyper::header::HeaderValue::from_static("*"),
);
res.headers_mut().insert(
hyper::header::ACCESS_CONTROL_ALLOW_METHODS,
hyper::header::HeaderValue::from_static("GET,POST,PUT,PATCH,DELETE"),
);
res.headers_mut().insert(
hyper::header::ACCESS_CONTROL_ALLOW_HEADERS,View on GitHub (pinned to 82976d349a)