gitbutlerapp/gitbutler · critical

failed to create logs dir

Error message

failed to create logs dir

What it means

logs::init() runs inside the Tauri setup hook and first creates the log directory with fs::create_dir_all(logs_dir).expect("failed to create logs dir"). logs_dir comes from but_path::app_log_dir() (~/Library/Logs/<identifier> on macOS, data-local dir/<identifier>/logs elsewhere); mkdir fails on permission denial, a read-only volume, or when a regular file already occupies the path. Because this is a panic in setup, the whole app startup aborts.

Solutions

  1. Check write permission on the log dir's parent and remove any regular file occupying the log dir path (ls -la the parent to spot it)
  2. Verify the volume is writable and has space (df -h)
  3. Set E2E_TEST_APP_DATA_DIR to a writable scratch directory to confirm the app works and the environment is the problem
  4. As a contributor: return a Result from logs::init and surface a dialog instead of panicking (see exampleFix)

Example fix

// before
fs::create_dir_all(logs_dir).expect("failed to create logs dir");

// after (init returns Result, setup closure propagates)
fs::create_dir_all(logs_dir)
    .with_context(|| format!("failed to create logs dir {}", logs_dir.display()))?;
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight the log dir before app startup
fn can_create_log_dir(dir: &std::path::Path) -> bool {
    std::fs::create_dir_all(dir).is_ok()
        && dir.metadata().map(|m| m.is_dir()).unwrap_or(false)
}

Prevention

When it happens

Trigger: Launching the GitButler desktop app where the resolved log directory cannot be created: restrictive permissions on ~/Library/Logs or %LOCALAPPDATA%, a leftover file named like the log directory, sandbox/antivirus interference, or a read-only/full disk.

Common situations: Broken user-profile permissions, corporate lockdown policies, disk-full or read-only home volumes, and stale artifacts occupying the target path after migrations.

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/5f163171e9ffb1b9. Report an issue: GitHub.

Appendix: source

Thrown at crates/gitbutler-tauri/src/logs.rs:14

use std::{fs, net::Ipv4Addr, path::Path, time::Duration};

use tauri::{AppHandle, Manager};
use tracing::{Level, instrument, metadata::LevelFilter, subscriber::set_global_default};
use tracing_appender::rolling::{RollingFileAppender, Rotation};
use tracing_subscriber::{Layer, filter::filter_fn, fmt::format::FmtSpan, layer::SubscriberExt};

pub fn init(
    app_handle: &AppHandle,
    logs_dir: &Path,
    performance_logging: bool,
    enable_tokio_console_log: bool,
) {
    fs::create_dir_all(logs_dir).expect("failed to create logs dir");

    let log_prefix = "GitButler";
    let log_suffix = "log";
    let max_log_files = 14;
    remove_old_logs(logs_dir).ok();
    let file_appender = RollingFileAppender::builder()
        .rotation(Rotation::DAILY)
        .max_log_files(max_log_files)
        .filename_prefix(log_prefix)
        .filename_suffix(log_suffix)
        .build(logs_dir)
        .expect("initializing rolling file appender failed");
    let (file_writer, guard) = tracing_appender::non_blocking(file_appender);
    // As the file-writer only checks `max_log_files` on file rotation, it basically never happens.
    // Run it now.
    prune_old_logs(logs_dir, Some(log_prefix), Some(log_suffix), max_log_files).ok();

    app_handle.manage(guard); // keep the guard alive for the lifetime of the app

View on GitHub (pinned to caf1f223d3)