{"record":{"id":"a1a4ebf5e7254032","repo":"gitbutlerapp/gitbutler","slug":"failed-to-create-config-dir","errorCode":null,"errorMessage":"failed to create config dir","messagePattern":"failed to create config dir","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/gitbutler-tauri/src/main.rs","lineNumber":54,"sourceCode":"            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)\n            .expect(\"failed to create app settings\");","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/gitbutler-tauri/src/main.rs#L36-L72","documentation":"Right after resolving it, main() creates the config directory with std::fs::create_dir_all(&config_dir).expect(\"failed to create config dir\"). create_dir_all fails with permission denied when the parent config root (~/.config, %APPDATA%) is not writable, on read-only or full filesystems, or when a non-directory entry (a regular file named gitbutler) already occupies the path. The panic aborts startup before any window appears.","triggerScenarios":"Starting the app when ~/.config (or the platform equivalent) is not writable by the current user, when a file already exists at ~/.config/gitbutler, or when the volume is read-only or out of space.","commonSituations":"Corporate machines with redirected/locked profiles, running as a user without write access to their own home, disk-full conditions, and leftover artifacts from earlier or broken installs.","solutions":["Fix write permissions on the parent config root (chmod u+w ~/.config, or icacls on Windows)","If a regular file blocks the path, move it aside (mv ~/.config/gitbutler ~/.config/gitbutler.bak) and relaunch","Free disk space or remount the volume read-write, then start the app again","As a contributor: propagate the error with context instead of expect (see exampleFix)"],"exampleFix":"# before: a file occupies the config dir path -> panic 'failed to create config dir'\nls -la ~/.config/gitbutler   # shows a regular file\n\n# after\nmv ~/.config/gitbutler ~/.config/gitbutler.bak && ./GitButler  # app recreates the directory","handlingStrategy":"validation","validationCode":"// Pre-flight the config dir before the app touches it\nfn config_dir_creatable(dir: &std::path::Path) -> bool {\n    std::fs::create_dir_all(dir).is_ok() && dir.is_dir()\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure the user can write to the platform config root (~/.config, %APPDATA%)","Remove stale files that occupy the intended directory path during migrations","Surface mkdir errors with path context instead of expect so support can act on them"],"tags":["tauri","filesystem","permissions","startup","config","panic"],"backgroundTag":"permission-denied","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}