{"record":{"id":"5d7ac8641eb7721b","repo":"mgth/LittleBigMouse","slug":"notfound","errorCode":"NotFound","errorMessage":"no grabbable mouse","messagePattern":"no grabbable mouse","errorType":"error_code","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"rust/crates/lbm-hook/src/hook/linux/evdev/router.rs","lineNumber":211,"sourceCode":"            dev.set_nonblocking(true)?;\n            match dev.grab() {\n                Ok(()) => {\n                    let id = dev.input_id();\n                    let name = dev.name().unwrap_or(\"?\").to_string();\n                    let settings = accel_cfg.for_device(id.vendor(), id.product(), &name);\n                    eprintln!(\"[LittleBigMouse.Hook] evdev: grabbed {name} ({path:?}, accel {:?} speed {})\",\n                        settings.profile, settings.speed);\n                    devices.push((\n                        path,\n                        dev,\n                        PointerAccel::new(settings.profile, settings.speed),\n                    ));\n                }\n                Err(e) => eprintln!(\"[LittleBigMouse.Hook] evdev: cannot grab {path:?}: {e}\"),\n            }\n        }\n        if devices.is_empty() {\n            return Err(std::io::Error::new(\n                std::io::ErrorKind::NotFound,\n                \"no grabbable mouse\",\n            ));\n        }\n        eprintln!(\n            \"[LittleBigMouse.Hook] evdev: observing {} keyboard(s) for ctrl-override\",\n            keyboards.len()\n        );\n\n        // Take over from where the cursor really is: ask the compositor (KWin\n        // scripting, logical coordinates — the zones' space), else where the\n        // previous arm left it. Only a first arm on a non-KDE compositor falls\n        // back to a neutral point (centre of the first main zone).\n        let (start, origin) = match probed {\n            Some(p) => (p, \"compositor\"),\n            None => match resume_at {\n                Some(p) => (p, \"previous position\"),\n                None => (","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/mgth/LittleBigMouse/blob/7a42f01d47d99d223b8ee33ba4019af82adf1c48/rust/crates/lbm-hook/src/hook/linux/evdev/router.rs#L193-L229","documentation":"The evdev hook arm routine scans candidate /dev/input device paths, tries to grab each one with EVIOCGRAB, and collects the successfully grabbed mice. If the final set is empty — either no mouse-like devices were found or every grab attempt failed — it returns io::ErrorKind::NotFound with \"no grabbable mouse\". This signals that the daemon cannot intercept any pointer input at the kernel level.","triggerScenarios":"Calling arm() when (a) no /dev/input/event* device matches the mouse filter (devices.is_empty() before grabbing), or (b) every EVIOCGRAB call returned Err (logged per-path as \"cannot grab {path:?}\") — typically EPERM/EACCES from lacking permissions, or the device vanished between enumeration and grab.","commonSituations":"Running the hook without root or without membership in the 'input' group; running inside a container/VM without /dev/input passthrough; Wayland session where the compositor holds the devices; udev rules denying EVIOCGRAB; device hot-unplugged mid-scan.","solutions":["Run the daemon with sufficient privileges: sudo, or add the user to the 'input' group (usermod -aG input $USER) and re-login.","Check /proc/bus/input/devices for a device with 'mouse' handlers and confirm the corresponding /dev/input/eventX node exists and is readable.","Look at the preceding eprintln lines \"evdev: cannot grab {path:?}\" to see the per-device errno and fix the root cause (permission, busy, gone).","If running under Wayland/sandbox, use a session with libinput access or run with elevated permissions; under containers, bind-mount /dev/input."],"exampleFix":"// before\nErr(e) => eprintln!(\"[LittleBigMouse.Hook] evdev: cannot grab {path:?}: {e}\"),\n// after (diagnose)\n// run: sudo ./lbm-hook   OR   usermod -aG input $USER && re-login\n// then re-run arm(); device grab succeeds and devices is non-empty","handlingStrategy":"try-catch","validationCode":"// before calling arm()\nif !std::path::Path::new(\"/dev/input\").read_dir().map(|d| d.count() > 0).unwrap_or(false) {\n    eprintln!(\"no /dev/input devices visible; are you in the 'input' group?\");\n}","typeGuard":null,"tryCatchPattern":"match arm() {\n    Ok(devices) => { /* proceed */ }\n    Err(e) if e.kind() == std::io::ErrorKind::NotFound => {\n        eprintln!(\"no grabbable mouse: check 'input' group membership and /dev/input access\");\n        // fall back to non-hook mode or exit cleanly\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Add the service user to the 'input' group and re-login before deploying.","Smoke-test device access with `head -c1 /dev/input/event0` in CI/setup scripts.","Log per-path grab errors (as the source already does) to distinguish permission vs missing-device causes."],"tags":["linux","evdev","input-devices","permissions"],"backgroundTag":"resource-not-found","analyzedSha":"7a42f01d47d99d223b8ee33ba4019af82adf1c48","analyzedAt":"2026-09-16T00:35:00.514Z","contentChangedAt":"2026-09-16T00:35:00.514Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}