janhq/jan · critical

Failed to get current exe path

Error message

Failed to get current exe path

What it means

This is a panic (.expect) from env::current_exe() inside schedule_mcp_start_task, which prepares to launch a stdio-based MCP server as a child process using the jan binary's directory as the working path. current_exe() fails under the same conditions as in spawn_detached: missing /proc on Linux, deleted binary inode, sandbox restrictions.

Source

Thrown at src-tauri/src/core/mcp/helpers.rs:404

            }

            Ok(())
        }
        Err(e) => {
            log::error!("Failed to start MCP server {name} on first attempt: {e}");
            Err(e)
        }
    }
}

async fn schedule_mcp_start_task<R: Runtime>(
    app: tauri::AppHandle<R>,
    servers: SharedMcpServers,
    name: String,
    config: Value,
) -> Result<(), String> {
    let app_path = get_jan_data_folder_path(app.clone());
    let exe_path = env::current_exe().expect("Failed to get current exe path");
    let exe_parent_path = exe_path
        .parent()
        .expect("Executable must have a parent directory");
    let bin_path = exe_parent_path.to_path_buf();

    let config_params = extract_command_args(&config)
        .ok_or_else(|| format!("Failed to extract command args from config for {name}"))?;

    if let (Some("http"), Some(url)) = (
        config_params.transport_type.as_deref(),
        config_params.url.clone(),
    ) {
        let transport = StreamableHttpClientTransport::with_client(
            reqwest::Client::builder()
                .default_headers({
                    // Map envs to request headers
                    let mut headers: tauri::http::HeaderMap = reqwest::header::HeaderMap::new();
                    for (key, value) in config_params.headers.iter() {

View on GitHub (pinned to fad3f12a14)

Solutions

  1. Ensure /proc/self/exe is accessible in the runtime environment.
  2. Avoid replacing the jan binary while MCP servers are being spawned.
  3. Cache the exe path at startup rather than re-resolving it per MCP server start.
  4. Fall back to std::env::current_dir().join(argv[0]) when current_exe fails.

Example fix

// before
let exe_path = env::current_exe().expect("Failed to get current exe path");

// after
let exe_path = env::current_exe().unwrap_or_else(|e| {
    log::warn!("current_exe() failed ({e}); using argv[0] fallback");
    PathBuf::from(std::env::args().next().unwrap_or_default())
});
Defensive patterns

Strategy: fallback

Validate before calling

// At startup, cache the exe path so MCP start tasks don't need current_exe
use std::sync::OnceLock;

static EXE_PATH: OnceLock<PathBuf> = OnceLock::new();

pub fn init_exe_path() {
    let _ = EXE_PATH.get_or_init(|| {
        std::env::current_exe().unwrap_or_else(|e| {
            log::warn!("current_exe failed at init: {e}");
            PathBuf::from(std::env::args().next().unwrap_or_default())
        })
    });
}

pub fn get_cached_exe_path() -> &'static PathBuf {
    EXE_PATH.get().expect("init_exe_path not called")
}

Try / catch

// Replace .expect with a fallback
let exe_path = match env::current_exe() {
    Ok(p) => p,
    Err(e) => {
        log::error!("current_exe() failed: {e}; MCP server spawn may fail");
        return Err(format!("Cannot resolve executable path: {e}"));
    }
};

Prevention

When it happens

Trigger: Starting an MCP server configured with command 'npx' or a local binary while running in a container without /proc. The jan binary was replaced by an updater while MCP servers are being initialized. Running in a minimal namespace/jail that blocks procfs.

Common situations: AppImage or Flatpak sandboxes. Docker dev containers without procfs. Post-update first launch where the old binary path no longer exists. SELinux denying readlink on /proc/self/exe.

Related errors


AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12). Data as JSON: /api/errors/b10a86531b2f6096. Report an issue: GitHub.