mgth/LittleBigMouse · critical
failed to start per-user local IPC
Error message
failed to start per-user local IPC: {error} What it means
The lbm-hook Rust daemon's main() starts its per-user local IPC server (ipc::server::start, or start_with_endpoint when LBM_HOOK_ENDPOINT is set) before entering its listen loop. If the server cannot bind/start, the Result is unwrapped with a panic! carrying the underlying IPC error, aborting the hook process at startup.
Solutions
- Remove the stale socket/endpoint file (or choose a fresh endpoint) and restart the hook.
- If LBM_HOOK_ENDPOINT is set, verify it points to an existing, writable location not already in use; unset it to fall back to the default per-user endpoint.
- Ensure the per-user runtime directory (XDG runtime dir / tmp equivalent) exists and is writable by the user running the hook.
- Kill any leftover hook/daemon process still bound to the endpoint before relaunching.
Example fix
// before LBM_HOOK_ENDPOINT=/run/user/1000/lbm-test ./lbm-hook // dir missing -> panic // after mkdir -p /run/user/1000/lbm-test && rm -f /run/user/1000/lbm.sock && ./lbm-hook
Defensive patterns
Strategy: validation
Validate before calling
// before launching the hook
if let Ok(ep) = std::env::var("LBM_HOOK_ENDPOINT") {
let p = std::path::Path::new(&ep);
if p.exists() { eprintln!("endpoint {} already exists (stale socket?) — remove or change it", ep); }
else if let Some(dir) = p.parent() {
if !dir.is_dir() { eprintln!("endpoint parent {} does not exist", dir.display()); }
}
} Type guard
fn endpoint_usable(ep: &str) -> bool {
let p = std::path::Path::new(ep);
!p.exists() && p.parent().map(|d| d.is_dir()).unwrap_or(false)
} Try / catch
match std::panic::catch_unwind(|| start_hook(shared)) {
Ok(()) => {},
Err(_) => eprintln!("[LittleBigMouse.Hook] IPC startup failed: remove stale socket or free the endpoint, then retry"),
} Prevention
- Clean up stale socket files on shutdown (and at startup) so a crashed instance does not block the next one.
- Only set LBM_HOOK_ENDPOINT for side-by-side testing, and point it at a dedicated writable directory.
- Ensure the per-user runtime directory (XDG_RUNTIME_DIR) exists and has correct ownership before launch.
- Adopt a single-instance lock or PID check so two hook processes never race for the same endpoint.
When it happens
Trigger: Running the hook binary when the IPC socket/endpoint cannot be created or bound — a stale socket file at the default endpoint path, LBM_HOOK_ENDPOINT set to an unusable path or address, a permission problem in the runtime directory, or another (leftover) process already holding the endpoint.
Common situations: A previous crashed hook instance left a stale socket file; side-by-side testing with LBM_HOOK_ENDPOINT pointing at a directory that does not exist or is not writable; running under a sandbox (Flatpak/systemd) without access to the default runtime dir; two hook instances racing on the same endpoint.
Related errors
AI-assisted analysis of mgth/LittleBigMouse@7a42f01d47 (2026-09-16).
Data as JSON: /api/errors/363a97d08893a6cf.
Report an issue: GitHub.
Appendix: source
Thrown at rust/crates/lbm-hook/src/main.rs:29
use littlebigmouse_hook::{daemon, hook, ipc, platform};
fn main() {
platform::init();
let shared: &'static Shared = SHARED.get_or_init(Shared::new);
// main() is the event-loop thread; record it before the server can accept a
// command that would signal it.
hook::register_main_thread(shared);
// `LBM_HOOK_ENDPOINT` overrides the per-session pipe/socket path, enabling
// side-by-side testing next to a running daemon (successor of the old
// LBM_HOOK_PORT override).
let (server, endpoint) = match std::env::var("LBM_HOOK_ENDPOINT") {
Ok(endpoint) => ipc::server::start_with_endpoint(shared, endpoint),
Err(_) => ipc::server::start(shared),
}
.unwrap_or_else(|error| panic!("failed to start per-user local IPC: {error}"));
let _ = shared.server.set(server);
eprintln!("[LittleBigMouse.Hook] listening on {endpoint}");
// C++ Program.cpp: UI mode (wait for socket commands) when launched by the UI
// (parent path contains "LittleBigMouse"); otherwise standalone — load the
// last saved layout and start hooking. `LBM_HOOK_UI` forces UI mode for tests.
let ui_mode = std::env::var_os("LBM_HOOK_UI").is_some()
|| platform::process::parent_process_path()
.map(|p| p.contains("LittleBigMouse"))
.unwrap_or(false);
if !ui_mode {
if let Some(path) = platform::paths::lbm_data_file("Current.xml") {
eprintln!(
"[LittleBigMouse.Hook] standalone mode: loading {}",
path.display()
);View on GitHub (pinned to 7a42f01d47)