GraphiteEditor/Graphite · error
Error setting Ctrl-C handler
Error message
Error setting Ctrl-C handler
What it means
Panic when ctrlc::set_handler fails while the desktop app installs its Ctrl-C/SIGINT handler that schedules AppEvent::Exit. The ctrlc crate reports an error primarily when a handler is already installed for this process (Error::MultipleHandlers), and on some platforms when the underlying OS signal registration call returns an error. Only one ctrl-c handler may exist per process.
Source
Thrown at desktop/src/app.rs:72
impl App {
pub(crate) fn init() {
Window::init();
}
pub(crate) fn new(
ui: UiInstance,
wgpu_context: WgpuContext,
app_event_receiver: Receiver<AppEvent>,
app_event_scheduler: AppEventScheduler,
preferences: Preferences,
launch_documents: Vec<PathBuf>,
) -> Self {
let ctrlc_app_event_scheduler = app_event_scheduler.clone();
ctrlc::set_handler(move || {
tracing::info!("Termination signal received, exiting...");
ctrlc_app_event_scheduler.schedule(AppEvent::Exit);
})
.expect("Error setting Ctrl-C handler");
let exiting = Arc::new(AtomicBool::new(false));
let rendering_app_event_scheduler = app_event_scheduler.clone();
let (start_render_sender, start_render_receiver) = std::sync::mpsc::sync_channel(1);
let exiting_clone = exiting.clone();
std::thread::spawn(move || {
let runtime = tokio::runtime::Runtime::new().unwrap();
loop {
let result = runtime.block_on(DesktopWrapper::execute_node_graph());
rendering_app_event_scheduler.schedule(AppEvent::NodeGraphExecutionResult(result));
let _ = start_render_receiver.recv_timeout(Duration::from_millis(10));
if exiting_clone.load(Ordering::Relaxed) {
break;
}
}
});
View on GitHub (pinned to c507b35645)
Solutions
- Ensure the app object that installs the Ctrl-C handler is created exactly once per process
- Search the dependency tree for other ctrlc crate users and consolidate to a single owner of the handler
- In tests, construct the app once or factor the handler installation out of the constructor
- If a second registration is genuinely needed, switch to a channel-based handler shared by both call sites
Example fix
// before
ctrlc::set_handler(move || {
tracing::info!("Termination signal received, exiting...");
ctrlc_app_event_scheduler.schedule(AppEvent::Exit);
})
.expect("Error setting Ctrl-C handler");
// after
if let Err(e) = ctrlc::set_handler(move || {
tracing::info!("Termination signal received, exiting...");
ctrlc_app_event_scheduler.schedule(AppEvent::Exit);
}) {
tracing::warn!("Ctrl-C handler not installed (already set or unsupported): {e}");
} Defensive patterns
Strategy: try-catch
Try / catch
match ctrlc::set_handler(move || {
tracing::info!("Termination signal received, exiting...");
ctrlc_app_event_scheduler.schedule(AppEvent::Exit);
}) {
Ok(()) => {}
Err(e) if e.kind() == ctrlc::ErrorKind::MultipleHandlers => {
tracing::warn!("Ctrl-C handler already set elsewhere; skipping");
}
Err(e) => tracing::warn!("Ctrl-C handler not installed: {e}"),
} Prevention
- Construct the app (and thus install the handler) exactly once per process, especially in tests
- Search the dependency tree for other ctrlc users before adding another handler
- Treat signal-handler installation as best-effort for a GUI app: log and continue rather than panic
When it happens
Trigger: App::new being constructed twice in one process (integration tests, embedders) so set_handler is called again; another crate or dependency in the same binary also registering a ctrlc handler; rare OS-level sigaction/SetConsoleCtrlHandler failures.
Common situations: Test harnesses that instantiate the full desktop App multiple times in a single test process; a dependency (CLI framework, tracing setup, CEF wrapper) that already owns the ctrl-c handler; embedding the desktop app inside another host process.
Related errors
- Failed to initialize on-disk resource storage
- Failed to get data directory
- Failed to create WGPU context
- InvalidData
- Failed to parse input type for #[implementation(...)]. Expec
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/e51663225e370a8f.
Report an issue: GitHub.