gitbutlerapp/gitbutler · error · anyhow::Error

ProjectHandle payload must decode to an absolute filesystem

Error message

ProjectHandle payload must decode to an absolute filesystem path, got '{}'

What it means

`ProjectHandle` encodes a repository location into a portable string payload. On decode, `encoded_str_to_path` base-decodes the payload and converts the bytes to a path via gix; a payload that decodes but is not an absolute path is rejected, because the handle must identify a workspace by absolute filesystem location.

Source

Thrown at crates/but-project-handle/src/project_handle.rs:112

}

impl TryFrom<ProjectHandle> for PathBuf {
    type Error = anyhow::Error;

    fn try_from(value: ProjectHandle) -> Result<Self, Self::Error> {
        value.into_path()
    }
}

fn encoded_str_to_path(encoded: &str) -> anyhow::Result<PathBuf> {
    let bytes = decode(encoded)?;
    let path = gix::path::try_from_byte_slice(&bytes)
        .map_err(anyhow::Error::from)
        .with_context(|| {
            format!("Encoded ProjectHandle payload is not a valid filesystem path: '{encoded}'")
        })?;
    if !path.is_absolute() {
        bail!(
            "ProjectHandle payload must decode to an absolute filesystem path, got '{}'",
            path.display()
        );
    }
    Ok(path.to_owned())
}

fn path_to_string(path: &Path) -> Result<String, anyhow::Error> {
    let bytes = gix::path::os_str_into_bstr(path.as_os_str())?;
    Ok(encode(bytes))
}

fn encode(bytes: &[u8]) -> String {
    URL_SAFE_NO_PAD.encode(bytes)
}

fn decode(encoded: &str) -> anyhow::Result<Vec<u8>> {
    URL_SAFE_NO_PAD

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Absolutize the path before encoding it into the handle (std::fs::canonicalize or std::path::absolute)
  2. Reject relative paths at the source (config loader, CLI parser) with a clear message
  3. Re-derive the handle from the repository's absolute workdir

Example fix

// before
let handle = ProjectHandle::from_path(Path::new("my-repo"))?; // relative -> bails on decode

// after
let abs = std::path::absolute(Path::new("my-repo"))?;
let handle = ProjectHandle::from_path(abs)?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust: only build handles from absolute paths
let abs = if path.is_absolute() {
    path.to_path_buf()
} else {
    std::path::absolute(path)?
};
let handle = ProjectHandle::from_path(abs)?;

Type guard

// Rust
fn is_valid_project_path(p: &std::path::Path) -> bool {
    p.is_absolute()
}

Try / catch

Catch the decode error, re-derive the path from the repository's absolute workdir (e.g. via canonicalize), rebuild the handle, and retry once.

Prevention

When it happens

Trigger: Creating or decoding a ProjectHandle whose payload encodes a relative path such as '.', '..', or 'my-repo' — typically a path captured with a different working directory or stored relative in config.

Common situations: Paths persisted from another machine or shell with a different cwd; config entries storing relative paths; tests and scripts using relative fixture paths.

Related errors


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