GitoxideLabs/gitoxide · error · anyhow::Error

Adding files requires a worktree directory that contains…

Error message

Adding files requires a worktree directory that contains them

What it means

Thrown by `stream` in gitoxide-core's archive command when `--add` paths were supplied but the repository is bare (no worktree). Adding files to the archive stream requires resolving paths against a real working directory via `repo.workdir()`, which is `None` for bare repositories.

Solutions

  1. Run the command in a non-bare clone that has a working tree
  2. Drop the `--add` flags and only stream from a tree/commit, which does not need a workdir
  3. Convert the bare repo: `git worktree add <dir> <ref>` and run against the worktree, or `git clone` a normal checkout

Example fix

// before (bare repo)
gix repo archive HEAD --add extra.txt
// after
gix repo archive HEAD  # stream tree contents only, no --add
Defensive patterns

Strategy: validation

Validate before calling

if add_paths.is_empty() {
    // safe: no workdir needed
} else if repo.workdir().is_none() {
    return Err("--add requires a non-bare repository with a worktree".into());
}

Type guard

fn supports_add_paths(repo: &gix::Repository) -> bool {
    repo.workdir().is_some()
}

Prevention

When it happens

Trigger: Calling `gix archive ... --add <path>` (via `stream`) inside or against a bare repository, or a repository opened with `--bare`/`open::Kind::Bare` so `repo.workdir()` returns `None`.

Common situations: Running the archive command on a server-side bare clone that only exists for hosting; CI checkout configured as bare to save space.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/d8f18303b85ba1bd. Report an issue: GitHub.

Appendix: source

Thrown at gitoxide-core/src/repository/archive.rs:34

    rev_spec: Option<&str>,
    mut progress: impl NestedProgress,
    Options {
        format,
        prefix,
        add_paths,
        files,
    }: Options,
) -> anyhow::Result<()> {
    let format = format.map_or_else(|| format_from_ext(destination_path), Ok)?;
    let object = repo.rev_parse_single(rev_spec.unwrap_or("HEAD"))?.object()?;
    let (modification_date, tree) = fetch_rev_info(object)?;

    let start = std::time::Instant::now();
    let (mut stream, index) = repo.worktree_stream(tree)?;
    if !add_paths.is_empty() {
        let root = gix::path::realpath(
            repo.workdir()
                .ok_or_else(|| anyhow!("Adding files requires a worktree directory that contains them"))?,
        )?;
        for path in add_paths {
            stream.add_entry_from_path(&root, &gix::path::realpath(&path)?, repo.object_hash())?;
        }
    }
    for (path, content) in files {
        stream.add_entry(gix::worktree::stream::AdditionalEntry {
            id: gix::hash::Kind::Sha1.null(),
            mode: gix::object::tree::EntryKind::Blob.into(),
            relative_path: path.into(),
            source: gix::worktree::stream::entry::Source::Memory(content.into()),
        });
    }

    let mut entries = progress.add_child("entries");
    entries.init(Some(index.entries().len()), gix::progress::count("entries"));
    let mut bytes = progress.add_child("written");
    bytes.init(None, gix::progress::bytes());

View on GitHub (pinned to e73179060b)