gitbutlerapp/gitbutler · error

Repository path is not valid UTF-8

Error message

Repository path is not valid UTF-8

What it means

Thrown by `setup_local_remote` when the repository's working directory path cannot be converted to `&str` (it is not valid UTF-8). GitButler stores the remote URL as a UTF-8 string, so non-UTF-8 paths are rejected.

Solutions

  1. Rename the repository directory (and parents) to ASCII/UTF-8-only names.
  2. Move the clone to a path without special characters, e.g. `~/code/myrepo`.
  3. Re-clone the repository into a UTF-8-safe location.

Example fix

// before
~/pr?jects/répo  (non-UTF-8 bytes)
// after
mv ~/projects/répo ~/projects/repo  # ensure valid UTF-8
but setup ~/projects/repo
Defensive patterns

Strategy: validation

Validate before calling

const p = path.resolve(repoPath);
if (!isUtf8(Buffer.from(p))) {
  throw new Error(`Repo path must be valid UTF-8: ${p}`);
}

Type guard

function isUtf8Path(p: string): boolean {
  return Buffer.from(p, 'utf8').toString('utf8') === p;
}

Try / catch

try {
  await run(['but', 'setup', repoPath]);
} catch (e) {
  if (String(e).includes('not valid UTF-8')) {
    console.error('Move/rename the repo to a UTF-8-only path.');
  }
  throw e;
}

Prevention

When it happens

Trigger: Running setup in a repository located under a directory with non-UTF-8 bytes in its name (e.g. Latin-1 encoded filenames on Linux).

Common situations: Linux systems with legacy filesystem encodings; paths created by tools using raw bytes; mounts with unusual naming; ZIP extracts with mis-encoded names.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/6d5e752b775cb364. Report an issue: GitHub.

Appendix: source

Thrown at crates/but/src/command/legacy/setup.rs:590

            .remote_default_name(gix::remote::Direction::Push)
            .is_none()
    {
        anyhow::bail!(
            "Neither found push remote found in workspace nor unambiguously in the Git repository configuration."
        )
    };

    Ok(true)
}

/// Creates a 'gb-local' remote pointing to this repository and creates tracking refs for the default branch.
fn setup_local_remote(repo: &gix::Repository, out: &mut OutputChannel) -> anyhow::Result<String> {
    let t = theme::get();
    let repo_url = repo
        .workdir()
        .ok_or_else(|| anyhow::anyhow!("Repository has no working directory"))?
        .to_str()
        .ok_or_else(|| anyhow::anyhow!("Repository path is not valid UTF-8"))?;

    if let Some(out) = out.for_human() {
        writeln!(
            out,
            "  {}",
            t.info
                .paint("No push remote found, creating gb-local remote...")
        )?;
    }

    edit_repo_config(repo, gix::config::Source::Local, |config| {
        let mut section = config.section_mut_or_create_new("remote", Some("gb-local".into()))?;
        section.push("url", repo_url)?;
        Ok(())
    })?;

    // Figure out what local branch is probably the default target
    let mut head_ref = repo.head()?;

View on GitHub (pinned to 58e5313667)