{"record":{"id":"a79b122d21fb53f5","repo":"gitbutlerapp/gitbutler","slug":"missing-config-dir","errorCode":null,"errorMessage":"missing config dir","messagePattern":"missing config dir","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/gitbutler-tauri/src/main.rs","lineNumber":53,"sourceCode":"            but_askpass::disable();\n            return runtime.block_on(but::handle_args(std::env::args_os()));\n        }\n    }\n    let performance_logging = std::env::var_os(\"GITBUTLER_PERFORMANCE_LOG\").is_some();\n    let tauri_debug_logging = std::env::var_os(\"GITBUTLER_TAURI_DEBUG_LOG\").is_some();\n\n    let mut tauri_context = generate_context!();\n    but_secret::secret::set_application_namespace(&tauri_context.config().identifier);\n\n    // Set the macOS notification bundle ID so notifications appear as GitButler.\n    #[cfg(target_os = \"macos\")]\n    {\n        if let Err(e) = notify_rust::set_application(&tauri_context.config().identifier) {\n            tracing::warn!(error = %e, \"Failed to set notification application\");\n        }\n    }\n\n    let config_dir = but_path::app_config_dir().expect(\"missing config dir\");\n    std::fs::create_dir_all(&config_dir).expect(\"failed to create config dir\");\n    let custom_settings = cfg!(feature = \"packaged-but-distribution\")\n        .then(but_settings::customization::packaged_but_binary);\n    // While it serves a function, this behavior is sub-optimal. The proper solution is to decouple:\n    // - Checking for updates from\n    // - Performing an update\n    // This way people can be informed that there is an update even if self-updating is not possible (i.e. installed via package manager).\n    let custom_settings = if cfg!(feature = \"disable-auto-updates\") {\n        but_settings::customization::merge_two(\n            but_settings::customization::disable_auto_update_checks(),\n            custom_settings,\n        )\n        .into()\n    } else {\n        custom_settings\n    };\n    let mut app_settings =\n        AppSettingsWithDiskSync::new_with_customization(config_dir.clone(), custom_settings)","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/gitbutler-tauri/src/main.rs#L35-L71","documentation":"gitbutler-tauri's main() resolves the settings directory with but_path::app_config_dir().expect(\"missing config dir\"). app_config_dir() wraps dirs::config_dir(), which returns None when the platform's config location cannot be determined: on Linux when $HOME is unset/empty or $XDG_CONFIG_HOME is set to a relative path, on macOS when no home directory exists, on Windows when the known-folder lookup fails. The expect converts that environment problem into an immediate startup panic; setting E2E_TEST_APP_DATA_DIR bypasses dirs::config_dir() entirely (but-path/src/lib.rs:93-100).","triggerScenarios":"Launching the desktop app from an environment without a resolvable config directory: HOME unset or empty (services, daemons, minimal containers, some CI runners), a relative XDG_CONFIG_HOME on Linux, or a broken user profile.","commonSituations":"Running the app from systemd/launchd/cron without a full user environment, inside minimal Docker images, after HOME was cleared, or with misconfigured XDG variables.","solutions":["Launch the app with a valid HOME (export HOME=/home/<user>)","Make XDG_CONFIG_HOME an absolute path or unset it so the default ~/.config is used","For automated runs, set E2E_TEST_APP_DATA_DIR to a writable directory - it overrides the lookup","As a contributor: replace the expect with ? so main() reports the failure cleanly (see exampleFix)"],"exampleFix":"// before\nlet config_dir = but_path::app_config_dir().expect(\"missing config dir\");\n\n// after\nlet config_dir = but_path::app_config_dir()\n    .context(\"could not resolve config dir (is $HOME set and absolute?)\")?;","handlingStrategy":"validation","validationCode":"// Environment pre-check mirroring dirs::config_dir() requirements\nfn config_dir_resolvable() -> bool {\n    if std::env::var_os(\"E2E_TEST_APP_DATA_DIR\").is_some() {\n        return true;\n    }\n    #[cfg(target_os = \"linux\")]\n    {\n        let home_ok = std::env::var_os(\"HOME\").is_some_and(|h| !h.is_empty());\n        let xdg_ok = std::env::var_os(\"XDG_CONFIG_HOME\")\n            .map(|v| std::path::Path::new(&v).is_absolute())\n            .unwrap_or(true);\n        home_ok && xdg_ok\n    }\n    #[cfg(not(target_os = \"linux\"))]\n    { true }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Launch desktop apps from full user sessions; services need HOME (or E2E_TEST_APP_DATA_DIR) set explicitly","Keep XDG_CONFIG_HOME absolute or unset","Prefer propagating the Result from but_path::app_config_dir() instead of expect in entry points"],"tags":["tauri","environment","home","xdg","startup","dirs","panic"],"backgroundTag":"missing-home-directory","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}