---
title: RepoError variants
description: Every git-operation failure variant, its miette diagnostic code, which Repo method produces it, and what the cause field contains.
tags: [reference, errors, git]
---
# `RepoError` variants
Every `Repo` method returns `Result<_, RepoError>`. The enum is
`#[non_exhaustive]`, so match with a `_` arm.
## What can I match on?
The **variant shape** is the public contract. The `cause` string is not: it
carries whatever gix or `git` stderr said, and it changes when the backend
changes. Assert on `matches!(err, RepoError::OpenFailed { .. })`, never on the
text.
`RepoError` does **not** implement `Clone`, unlike `ProviderError`. It holds a
live `std::io::Error` in the `Io` variant and a `CredentialError` in `Auth`.
## Which method produces which variant?
| `Io` | reserved; no method in the crate constructs it today |
| `OpenFailed` | `open` |
| `InitFailed` | `init` |
| `CloneFailed` | `clone` |
| `FetchFailed` | `fetch` |
| `CheckoutFailed` | `checkout` |
| `CommitFailed` | `commit` |
| `StatusFailed` | `status` |
| `WalkFailed` | `walk`, and **`blame`** |
| `DiffFailed` | `diff` |
| `PushFailed` | `push` |
| `RevspecNotFound` | `walk`, `diff`, `blame`, `checkout` |
| `DirtyWorkingTree` | `checkout` |
| `Auth` | `clone`, `fetch`, `push` |
## `OpenFailed` / `InitFailed`
```
could not open repository at `{path}`: {cause}
could not init repository at `{path}`: {cause}
```
Codes `rtb_forge::git::open_failed` and `rtb_forge::git::init_failed`. `path` is
the path you passed. `cause` is gix's stringified error, or a
`spawn_blocking join error: …` string if the background task itself failed.
`OpenFailed` is what you get for a directory that is not a repository, and for
a repository whose `.git` cannot be read.
## `CloneFailed`
```
could not clone `{url}`: {cause}
```
Code `rtb_forge::git::clone_failed`. The `cause` prefix tells you how far it
got:
| `prepare: …` | gix could not parse the URL or prepare the destination |
| `fetch: …` | gix refs negotiation or object download failed |
| `checkout: …` | gix could not write the working tree |
| `spawn \`git clone\`: …` | the `git` binary could not be launched (authenticated clones only) |
| `git clone: …` | `git clone` exited non-zero; the rest is its stderr |
| `open post-clone: …` | the clone succeeded but the result could not be opened |
## `FetchFailed` / `PushFailed`
```
could not fetch from `{remote}`: {cause}
could not push `{refspec}` to `{remote}`: {cause}
```
Codes `rtb_forge::git::fetch_failed` and `rtb_forge::git::push_failed`. `cause`
is either `spawn \`git …\`: <os error>` or `git …: <stderr>`.
When a `spawn_blocking` join fails, `remote` and `refspec` are the **empty
string** rather than the values you passed, because the owned copies were moved into
the failed task. Do not rely on those fields being populated in every case.
## `CheckoutFailed`
```
could not check out `{revspec}`: {cause}
```
Code `rtb_forge::git::checkout_failed`. Reached when `git checkout` exits
non-zero for a reason that does not look like a missing revspec. See
`RevspecNotFound` below for how that call is made.
## `CommitFailed`
```
could not create commit: {cause}
```
Code `rtb_forge::git::commit_failed`. There is no `path` or `message` field;
everything is in `cause`:
| `no paths supplied — commit requires at least one path to stage` | you passed an empty slice |
| `spawn \`git add\`: …` | the `git` binary is missing or not executable |
| `git add: …` | `git add` rejected a path |
| `git commit: …` | nothing staged, no configured identity, a hook refused, … |
| `git rev-parse: …` | the commit was created but its OID could not be read |
| `oid utf-8: …` | `git rev-parse` produced non-UTF-8 output |
Note the ordering: a `git rev-parse` failure means the commit **did** happen.
Do not treat `CommitFailed` as proof the repository is unchanged.
## `StatusFailed` / `WalkFailed` / `DiffFailed`
```
could not compute status: {cause}
could not walk commits: {cause}
could not diff trees: {cause}
```
Codes `rtb_forge::git::status_failed`, `rtb_forge::git::walk_failed`,
`rtb_forge::git::diff_failed`.
`WalkFailed` covers more than its name suggests. It is produced by:
- `walk` for an unsupported revspec kind, with `unsupported revspec kind: …`.
- `walk` for object-store failures partway through, delivered as an `Err` item
on the stream rather than from the `walk()` call itself.
- **`blame`** for every failure that is not a missing revspec: resource-cache
setup, commit lookup, gix-blame internals. There is no `BlameFailed` variant.
## `RevspecNotFound`
```
revspec `{revspec}` not found
```
Code `rtb_forge::git::revspec_not_found`. Produced by `walk`, `diff`, `blame`
and `checkout`.
The `revspec` field is not always a bare revspec:
- From `blame`, a missing file is reported as `RevspecNotFound` with a
`revspec` of the form `<file path> at <revspec>`. There is no separate
file-not-found variant, so you cannot distinguish "bad revision" from "file
absent at that revision" by variant alone.
- From `checkout`, the classification comes from matching `git`'s stderr
against `did not match any`, `unknown revision`, `not a valid object name`
and `pathspec`. Under a non-English locale those phrases do not appear and
the same situation arrives as `CheckoutFailed` instead.
## `DirtyWorkingTree`
```
working tree is dirty: {n} path(s) need attention
```
Code `rtb_forge::git::dirty_working_tree`. Carries `paths: Vec<PathBuf>`, the
staged bucket first, then the unstaged one.
Only `checkout` with `force = false` produces it. Because
[`RepoStatus::staged` is always empty](05-repo-operations.md#repostatusstaged-is-always-empty),
the staged half contributes nothing and the list is effectively the unstaged
paths.
## `Auth`
```
credential resolution failed: {0}
```
Code `rtb_forge::git::auth`. Wraps `rtb_credentials::CredentialError` as a
`#[source]`, so the underlying reason (env var unset, keychain locked, literal
refused under `CI=true`) is available through `std::error::Error::source`.
This variant means the credential could not be **resolved locally**. It does
not mean the remote rejected it: a token the server refuses produces
`CloneFailed` / `FetchFailed` / `PushFailed` carrying git's stderr.