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
- Ensure XDG_DATA_HOME points to an absolute, writable path on Linux.
- Verify the app's bundle identifier is set in tauri.conf.json identifier field.
- Set HOME or USERPROFILE so the default data dir can be derived.
- 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
- Replace .expect with .ok() and propagate None up to callers that can skip lock operations.
- Cache the data dir at startup so lock operations don't re-resolve it.
- Ensure XDG_DATA_HOME / APPDATA / bundle ID are correctly configured.
- Test MCP lock operations in a minimal container to catch env issues.
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
- Failed to determine the home directory
- Failed to serialize MCP settings
- Failed to get current exe path
- Executable must have a parent directory
- cannot resolve current exe
AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12).
Data as JSON: /api/errors/ff1ef2c14fd72983.
Report an issue: GitHub.