mgth/LittleBigMouse · error · std::io::Error
NotFound
NotFound
Error message
no per-user runtime directory
What it means
default_endpoint() builds the IPC socket path by asking lbm_ipc::endpoint::socket_path to pick between XDG_RUNTIME_DIR and the fallback data directory. When neither yields a usable per-user path, it returns NotFound with "no per-user runtime directory". The IPC server cannot be started or located without a valid socket path.
Solutions
- Ensure XDG_RUNTIME_DIR is set, e.g. export XDG_RUNTIME_DIR=/run/user/$(id -u) before launching the daemon/service.
- In systemd units add Environment=XDG_RUNTIME_DIR=/run/user/%U (or use ExecStart with a user session slice so the variable is inherited).
- If relying on the fallback, make sure the lbm data directory exists and is writable by the current user.
- Pre-create the runtime directory with correct ownership: install -d -m 700 -o $USER /run/user/$(id -u).
Example fix
# before (fails in systemd unit) ExecStart=/usr/bin/littlebigmouse # after ExecStart=/usr/bin/littlebigmouse Environment=XDG_RUNTIME_DIR=/run/user/%U
Defensive patterns
Strategy: validation
Validate before calling
// before starting the IPC server
if std::env::var_os("XDG_RUNTIME_DIR").map(|v| v.is_empty()).unwrap_or(true) {
std::env::set_var("XDG_RUNTIME_DIR", format!("/run/user/{}", nix::unistd::getuid()));
} Try / catch
match default_endpoint() {
Ok(ep) => listen(ep),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
eprintln!("XDG_RUNTIME_DIR missing; set it to /run/user/$UID");
std::process::exit(1);
}
Err(e) => return Err(e.into()),
} Prevention
- In systemd units, always set Environment=XDG_RUNTIME_DIR=/run/user/%U.
- Never launch daemons via bare `sudo` (which strips env); use sudo -E or systemd user services.
- Assert the runtime dir exists and is writable at startup, before creating the socket.
When it happens
Trigger: Calling default_endpoint() when the XDG_RUNTIME_DIR environment variable is unset/empty AND the fallback lbm_data_file("") path is also unavailable (e.g. socket_path returned None), so there is no writable per-user directory to place the Unix domain socket.
Common situations: Running the daemon from a systemd unit or cron job with a minimal environment where XDG_RUNTIME_DIR is not exported; su/sudo dropping the env var; non-standard login shells; running as a system user with no XDG session and unwritable data dir.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
AI-assisted analysis of mgth/LittleBigMouse@7a42f01d47 (2026-09-16).
Data as JSON: /api/errors/75f5ee814674c970.
Report an issue: GitHub.
Appendix: source
Thrown at rust/crates/lbm-hook/src/ipc/server.rs:458
pub async fn run(self, server: ServerHandle, _shared: &'static Shared) {
let semaphore = Arc::new(Semaphore::new(MAX_CLIENTS));
while let Ok((stream, _)) = self.listener.accept().await {
let Ok(permit) = semaphore.clone().try_acquire_owned() else {
continue;
};
tokio::spawn(run_connection(stream, server.clone(), permit));
}
let _ = std::fs::remove_file(&self.path);
}
}
pub fn default_endpoint() -> io::Result<String> {
let runtime = std::env::var_os("XDG_RUNTIME_DIR").map(PathBuf::from);
let data = crate::platform::paths::lbm_data_file("");
lbm_ipc::endpoint::socket_path(runtime.as_deref(), data.as_deref())
.map(|path| path.to_string_lossy().into_owned())
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "no per-user runtime directory"))
}
}
View on GitHub (pinned to 7a42f01d47)