janhq/jan · critical

Failed to get app data dir

Error message

Failed to get app data dir

What it means

This is a panic (.expect) from Tauri's app.path().app_data_dir() inside get_lock_file_path(), the helper used by create_lock_file, read_lock_file, and delete_lock_file. app_data_dir() resolves the platform data directory (XDG_DATA_HOME/app_id on Linux, %APPDATA%/app_id on Windows, ~/Library/Application Support/app_id on macOS). It fails when the OS-level directory cannot be determined.

Source

Thrown at src-tauri/src/core/mcp/lockfile.rs:21

use std::path::PathBuf;
use tauri::{AppHandle, Manager, Runtime};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpLockFile {
    pub pid: u32,
    #[serde(default)]
    pub jan_pid: u32,
    pub port: u16,
    pub server_name: String,
    pub created_at: String,
    pub hostname: String,
}

fn get_lock_file_path<R: Runtime>(app: &AppHandle<R>, port: u16) -> PathBuf {
    let app_data_dir = app
        .path()
        .app_data_dir()
        .expect("Failed to get app data dir");
    app_data_dir.join(format!("mcp_lock_{}.json", port))
}

pub fn create_lock_file<R: Runtime>(
    app: &AppHandle<R>,
    port: u16,
    server_name: &str,
    pid: u32,
) -> Result<(), String> {
    let lock_path = get_lock_file_path(app, port);

    // Ensure parent directory exists
    if let Some(parent) = lock_path.parent() {
        fs::create_dir_all(parent)
            .map_err(|e| format!("Failed to create lock file directory: {}", e))?;
    }

    let lock = McpLockFile {

View on GitHub (pinned to fad3f12a14)

Solutions

  1. Ensure XDG_DATA_HOME points to an absolute, writable path on Linux.
  2. Verify the app's bundle identifier is set in tauri.conf.json identifier field.
  3. Set HOME or USERPROFILE so the default data dir can be derived.
  4. Replace .expect with a fallback to a known directory.

Example fix

// before
let app_data_dir = app.path().app_data_dir().expect("Failed to get app data dir");

// after
let app_data_dir = app.path().app_data_dir().unwrap_or_else(|e| {
    log::warn!("app_data_dir failed ({e}); falling back to home/.jan");
    dirs::home_dir().unwrap_or_default().join(".jan")
});
Defensive patterns

Strategy: fallback

Validate before calling

// Before lock file operations, verify the data dir is resolvable
fn verify_app_data_dir<R: Runtime>(app: &AppHandle<R>) -> Result<PathBuf, String> {
    app.path().app_data_dir()
        .map_err(|e| format!("Cannot resolve app data dir: {e}"))
}

// In the calling code:
let dir = verify_app_data_dir(app)?;
let lock_path = dir.join(format!("mcp_lock_{}.json", port));

Try / catch

// Replace .expect with graceful degradation
fn get_lock_file_path<R: Runtime>(app: &AppHandle<R>, port: u16) -> Option<PathBuf> {
    let dir = app.path().app_data_dir().ok()?;
    Some(dir.join(format!("mcp_lock_{}.json", port)))
}

Prevention

When it happens

Trigger: XDG_DATA_HOME is set to an invalid or non-absolute path on Linux. APPDATA env var is unset on Windows. Running in a sandboxed environment where the macOS app support path is restricted. The Tauri identifier/bundle ID is misconfigured, preventing path resolution.

Common situations: Linux containers without XDG_DATA_HOME set (defaults to ~/.local/share but HOME may be unset). Windows service accounts without a roaming profile. macOS App Sandbox misconfiguration. Missing CFBundleIdentifier in the Info.plist.

Related errors


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