janhq/jan · critical
cannot resolve current exe
Error message
cannot resolve current exe
What it means
This is a panic (.expect) from std::env::current_exe() inside spawn_detached(), the function that re-launches the jan CLI binary as a background server process. current_exe() can fail when the OS cannot resolve the running binary's path — on Linux this requires /proc/self/exe to be readable; on macOS it uses _NSGetExecutablePath; on Windows it queries GetModuleFileName. When it fails the process aborts immediately with no recovery.
Source
Thrown at src-tauri/src/bin/jan-cli.rs:720
return all[selection_items[0].0].0.clone();
}
let labels: Vec<String> = selection_items.iter().map(|(_, label)| label.clone()).collect();
let selection = dialoguer::Select::new()
.with_prompt("Choose a model")
.items(&labels)
.default(0)
.interact()
.unwrap_or_else(|_| std::process::exit(1));
all[selection_items[selection].0].0.clone()
}
// ── Detached spawn ─────────────────────────────────────────────────────────
fn spawn_detached(model_id: &str, args: &ServeArgs) {
let exe = std::env::current_exe().expect("cannot resolve current exe");
// Rebuild argv from ServeArgs fields so we have full control
// (avoids needing to filter --detach/-d from the raw OS args).
// Use --flag=value format throughout to avoid negative numbers being
// misinterpreted as short flags (e.g. --n-gpu-layers -1 → -1 looks like a flag).
let mut argv: Vec<String> = vec!["serve".into(), model_id.to_string()];
if let Some(p) = &args.model_path { argv.push(format!("--model-path={p}")); }
if let Some(b) = &args.bin { argv.push(format!("--bin={b}")); }
argv.push(format!("--port={}", args.port));
if let Some(m) = &args.mmproj { argv.push(format!("--mmproj={m}")); }
if args.embedding { argv.push("--embedding".into()); }
argv.push(format!("--timeout={}", args.timeout));
argv.push(format!("--n-gpu-layers={}", args.n_gpu_layers));
argv.push(format!("--ctx-size={}", args.ctx_size));
argv.push(format!("--threads={}", args.threads));
if !args.api_key.is_empty() { argv.push(format!("--api-key={}", args.api_key)); }
if args.fit { argv.push("--fit".into()); }
if args.verbose { argv.push("--verbose".into()); }View on GitHub (pinned to fad3f12a14)
Solutions
- Ensure /proc is mounted and /proc/self/exe is readable in container/namespace environments.
- Avoid deleting or replacing the jan binary while a serve session is running.
- For headless/containerized use, set the executable path explicitly via an env var instead of relying on current_exe.
- Run `jan serve` directly (non-detached) if detached spawn is not needed.
Example fix
// before
let exe = std::env::current_exe().expect("cannot resolve current exe");
// after
let exe = std::env::current_exe().unwrap_or_else(|e| {
eprintln!("Warning: current_exe() failed ({e}), falling back to argv[0]");
std::env::args().next().unwrap_or("jan".into()).into()
}); Defensive patterns
Strategy: fallback
Validate before calling
// Before spawning detached, verify the exe is resolvable
use std::env;
fn check_exe_resolvable() -> bool {
env::current_exe().is_ok()
}
// In a test or startup check:
if !check_exe_resolvable() {
eprintln!("Warning: current_exe() will fail in this environment; detached spawn will crash.");
} Try / catch
// Replace .expect with a graceful fallback
let exe = match std::env::current_exe() {
Ok(path) => path,
Err(e) => {
eprintln!("current_exe() failed: {e}");
// Fallback: use argv[0] resolved against PATH
let argv0 = std::env::args().next().unwrap_or_else(|| "jan".into());
which::which(&argv0).unwrap_or_else(|_| PathBuf::from(argv0))
}
}; Prevention
- Ensure /proc is mounted in container environments.
- Do not delete or overwrite the jan binary while a serve session is active.
- For containerized use, pass the exe path via an env var as an explicit override.
- Add a startup self-test that calls current_exe() and warns if it fails.
When it happens
Trigger: Running inside a container or chroot where /proc is not mounted (Linux). The executable file was deleted or replaced while the process is running (inode gone). Running under a statically-linked binary in a minimal namespace. Symlink chains that exceed MAX_SYMLINKS. Unusual sandbox environments that block procfs reads.
Common situations: CI runners with minimal container images (distroless, scratch). Dev containers or Docker dev environments without /proc. Package managers replacing the binary mid-run during an update. AppImage or Flatpak sandboxes restricting procfs.
Related errors
- Failed to get current exe path
- clone log file
- Failed to determine the home directory
- Failed to serialize MCP settings
- Executable must have a parent directory
AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12).
Data as JSON: /api/errors/2fc79aa5b1740f89.
Report an issue: GitHub.