espanso/espanso · error
unable to obtain executable path
Error message
unable to obtain executable path
What it means
get_portable_config_dir panics with 'unable to obtain executable path' when std::env::current_exe() returns Err. This function checks whether a '.espanso' folder exists next to the running executable to enable portable mode. current_exe fails when the OS cannot report the executable's path (e.g. the binary was deleted or replaced while running, or the platform facility like /proc/self/exe is unavailable).
Source
Thrown at espanso/src/path/mod.rs:153
// Legacy macOS location in ~/Library/Preferences/espanso
debug!(
"detected legacy config directory location at {}",
legacy_mac_dir.display()
);
Some(legacy_mac_dir)
} else if let Some(config_dir) = get_default_config_dir() {
debug!(
"detected default config directory at {}",
config_dir.display()
);
Some(config_dir)
} else {
None
}
}
fn get_portable_config_dir() -> Option<PathBuf> {
let espanso_exe_path = std::env::current_exe().expect("unable to obtain executable path");
let exe_dir = espanso_exe_path.parent();
if let Some(parent) = exe_dir {
let config_dir = parent.join(".espanso");
if config_dir.is_dir() {
return Some(config_dir);
}
}
None
}
fn get_home_espanso_dir() -> Option<PathBuf> {
if let Some(home_dir) = dirs::home_dir() {
let config_espanso_dir = home_dir.join(".espanso");
if config_espanso_dir.is_dir() {
return Some(config_espanso_dir);
}
}
NoneView on GitHub (pinned to e6c3736675)
Solutions
- Restart espanso from the installed binary after any update so current_exe resolves again.
- Mount /proc (Linux) or provide the platform facility current_exe needs if running in a minimal container.
- Use the --config-dir flag to bypass the portable config detection entirely.
- Ensure the on-disk binary is not deleted/renamed while a process is still using it (update atomically, then restart).
Example fix
// before # updater deletes binary then notifies running instance rm /usr/bin/espanso && killall espanso # old process panics on config lookup // after mv /usr/bin/espanso /usr/bin/espanso.new && install -m755 espanso /usr/bin/espanso && killall espanso # old exe still resolvable until restart
Defensive patterns
Strategy: try-catch
Validate before calling
if let Err(e) = std::env::current_exe() {
eprintln!("cannot resolve executable path: {e}; use --config-dir");
} Type guard
fn exe_path_resolvable() -> bool {
std::env::current_exe().map(|p| p.is_file()).unwrap_or(false)
} Try / catch
let exe_path = std::env::current_exe().unwrap_or_else(|e| {
eprintln!("current_exe failed: {e}");
std::process::exit(1);
}); Prevention
- Update the binary atomically and restart the process afterwards
- Keep the installed executable on disk for the lifetime of the running process
- Mount /proc in Linux containers
- Use --config-dir to skip portable detection when exe resolution is unreliable
When it happens
Trigger: Calling get_config_dir/resolve_paths when the espanso binary has been deleted or renamed after launch (in-place upgrade), when executing via an environment where current_exe is unsupported, or in containers/jails lacking /proc. The binary itself being a bare name with no parent directory can also contribute to downstream failures.
Common situations: Package managers or update scripts replacing the espanso binary while it runs, self-update tooling deleting the executable before spawning the new one, running in minimal Docker images without /proc mounted, exotic interpreters/wrappers that break exe path resolution.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- unable to obtain runtime directory path
- unable to create reference to lock file: {}
- unable to unlock lock_file: {:?}
- unable to write preference for key {key}
- unable to remove dirty package directory
AI-assisted analysis of espanso/espanso@e6c3736675 (2026-09-06).
Data as JSON: /api/errors/0ec9b0652e15b526.
Report an issue: GitHub.