gitbutlerapp/gitbutler · critical

failed to create config dir

Error message

failed to create config dir

What it means

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.

Source

Thrown at crates/gitbutler-tauri/src/main.rs:54

            return runtime.block_on(but::handle_args(std::env::args_os()));
        }
    }
    let performance_logging = std::env::var_os("GITBUTLER_PERFORMANCE_LOG").is_some();
    let tauri_debug_logging = std::env::var_os("GITBUTLER_TAURI_DEBUG_LOG").is_some();

    let mut tauri_context = generate_context!();
    but_secret::secret::set_application_namespace(&tauri_context.config().identifier);

    // Set the macOS notification bundle ID so notifications appear as GitButler.
    #[cfg(target_os = "macos")]
    {
        if let Err(e) = notify_rust::set_application(&tauri_context.config().identifier) {
            tracing::warn!(error = %e, "Failed to set notification application");
        }
    }

    let config_dir = but_path::app_config_dir().expect("missing config dir");
    std::fs::create_dir_all(&config_dir).expect("failed to create config dir");
    let custom_settings = cfg!(feature = "packaged-but-distribution")
        .then(but_settings::customization::packaged_but_binary);
    // While it serves a function, this behavior is sub-optimal. The proper solution is to decouple:
    // - Checking for updates from
    // - Performing an update
    // This way people can be informed that there is an update even if self-updating is not possible (i.e. installed via package manager).
    let custom_settings = if cfg!(feature = "disable-auto-updates") {
        but_settings::customization::merge_two(
            but_settings::customization::disable_auto_update_checks(),
            custom_settings,
        )
        .into()
    } else {
        custom_settings
    };
    let mut app_settings =
        AppSettingsWithDiskSync::new_with_customization(config_dir.clone(), custom_settings)
            .expect("failed to create app settings");

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Fix write permissions on the parent config root (chmod u+w ~/.config, or icacls on Windows)
  2. If a regular file blocks the path, move it aside (mv ~/.config/gitbutler ~/.config/gitbutler.bak) and relaunch
  3. Free disk space or remount the volume read-write, then start the app again
  4. As a contributor: propagate the error with context instead of expect (see exampleFix)

Example fix

# before: a file occupies the config dir path -> panic 'failed to create config dir'
ls -la ~/.config/gitbutler   # shows a regular file

# after
mv ~/.config/gitbutler ~/.config/gitbutler.bak && ./GitButler  # app recreates the directory
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight the config dir before the app touches it
fn config_dir_creatable(dir: &std::path::Path) -> bool {
    std::fs::create_dir_all(dir).is_ok() && dir.is_dir()
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/a1a4ebf5e7254032. Report an issue: GitHub.