{"record":{"id":"ee3b5f6a11ff27ab","repo":"linebender/druid","slug":"failed-to-initialize-the-keyboard-event-loop","errorCode":null,"errorMessage":"failed to initialize the keyboard event loop!","messagePattern":"failed to initialize the keyboard event loop!","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"druid-shell/src/backend/wayland/keyboard.rs","lineNumber":234,"sourceCode":"\nimpl Default for State {\n    fn default() -> Self {\n        let (apptx, apprx) = calloop::channel::channel::<KeyEvent>();\n        let (tx, rx) = calloop::channel::channel::<(\n            u32,\n            wl_keyboard::Event,\n            calloop::channel::Sender<KeyEvent>,\n        )>();\n        let state = Self {\n            apptx,\n            apprx: std::cell::RefCell::new(Some(apprx)),\n            tx,\n        };\n\n        std::thread::spawn(move || {\n            let mut eventloop: calloop::EventLoop<(calloop::LoopSignal, Keyboard)> =\n                calloop::EventLoop::try_new()\n                    .expect(\"failed to initialize the keyboard event loop!\");\n            let signal = eventloop.get_signal();\n            let handle = eventloop.handle();\n            let repeat = calloop::timer::Timer::<CachedKeyPress>::new().unwrap();\n            handle\n                .insert_source(rx, {\n                    let repeater = repeat.handle();\n                    move |event, _ignored, state| {\n                        let event = match event {\n                            calloop::channel::Event::Closed => {\n                                tracing::info!(\"keyboard event loop closed shutting down\");\n                                state.0.stop();\n                                return;\n                            }\n                            calloop::channel::Event::Msg(keyevent) => keyevent,\n                        };\n                        state.1.consume(event.0, event.1, event.2);\n                        match &state.1.last_key_press {\n                            None => repeater.cancel_all_timeouts(),","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/linebender/druid/blob/0f8b1195e4e073f9597f2865299c3d18f8e4005f/druid-shell/src/backend/wayland/keyboard.rs#L216-L252","documentation":"The Wayland keyboard backend spawns a background thread running a calloop event loop for key-repeat/input handling. If `calloop::EventLoop::try_new()` fails (event loop infrastructure could not be created, typically an epoll/fd allocation failure), the thread panics with expect and this message, killing keyboard input for the session.","triggerScenarios":"EventLoop::try_new() returning Err due to epoll_create failure — exhausted file descriptors, restrictive seccomp/sandbox blocking epoll, or memory exhaustion — during keyboard backend initialization (Keyboard::default).","commonSituations":"Running under Flatpak/Snap or container sandboxes with tight fd or syscall limits; hitting RLIMIT_NOFILE in long-running sessions with many fd leaks; embedded/minimal Wayland setups lacking epoll support.","solutions":["Check the process file-descriptor limit (`ulimit -n`) and raise it; look for fd leaks in the app.","Verify the sandbox (Flatpak/Snap/seccomp profile) permits epoll syscalls (epoll_create1).","Run the app outside the sandbox/container to confirm the cause, then adjust the profile.","Update druid/druid-shell to a version that handles this failure gracefully instead of panicking, or patch the expect into a logged error and degraded keyboard mode.","Check system memory; epoll allocation can fail under OOM pressure."],"exampleFix":"// before\nlet mut eventloop = calloop::EventLoop::try_new()\n    .expect(\"failed to initialize the keyboard event loop!\");\n\n// after\nlet mut eventloop = match calloop::EventLoop::try_new() {\n    Ok(el) => el,\n    Err(e) => { tracing::error!(\"keyboard event loop init failed: {e}\"); return; }\n};","handlingStrategy":"try-catch","validationCode":"// preflight: ensure epoll and fd headroom before spawning the keyboard thread\nlet can_epoll = unsafe { libc::epoll_create1(0) } >= 0;\nlet fd_headroom = {\n    let mut lim = libc::rlimit { rlim_cur: 0, rlim_max: 0 };\n    unsafe { libc::getrlimit(libc::RLIMIT_NOFILE, &mut lim) };\n    lim.rlim_cur > 256\n};","typeGuard":null,"tryCatchPattern":"// in patched builds, replace expect with graceful degradation:\nmatch calloop::EventLoop::try_new() {\n    Ok(el) => run_keyboard_loop(el),\n    Err(e) => log::error!(\"keyboard loop unavailable: {e}\"), // no key repeat\n}","preventionTips":["Raise RLIMIT_NOFILE and fix fd leaks in long-running Wayland sessions.","Configure Flatpak/Snap/seccomp to allow epoll_create1 and timerfd.","Keep druid-shell updated for improved wayland backend error handling.","Monitor startup logs for fd exhaustion symptoms."],"tags":["wayland","linux","calloop","keyboard","panic","fd-exhaustion"],"backgroundTag":"module-init-failed","analyzedSha":"0f8b1195e4e073f9597f2865299c3d18f8e4005f","analyzedAt":"2026-09-10T14:27:34.582Z","contentChangedAt":"2026-09-10T14:27:34.582Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}