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.

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)

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.