gitbutlerapp/gitbutler · error
Cannot hash directory entries that aren't files or symlinks
Error message
Cannot hash directory entries that aren't files or symlinks
What it means
id_or_hash_from_worktree() hashes a worktree path's content to an object id. Directories cannot be hashed as blobs, so when the path is neither a regular file nor a symlink (e.g. a directory or special file), the function bails. It is used when computing effective merged changes.
Solutions
- Skip directory-typed paths before hashing (check metadata kind first)
- Handle submodule entries through their commit-id path rather than content hashing
- Re-check worktree state and retry if the file was concurrently replaced
- Ensure the expected entry is actually a file: git status / re-read the index
Example fix
// before
let id = id_or_hash_from_worktree(repo, path)?;
// after
let id = if path.is_dir() {
eprintln!("{path:?} is a directory; skipping hash");
None
} else {
Some(id_or_hash_from_worktree(repo, path)?)
}; Defensive patterns
Strategy: validation
Validate before calling
fn hashable(path: &Path) -> bool {
path.symlink_metadata().map(|m| m.is_file() || m.is_symlink()).unwrap_or(false)
} Type guard
if path.is_dir() { skip(); } else { hash(path)?; } Try / catch
match id_or_hash_from_worktree(repo, path) {
Err(e) if e.to_string().contains("aren't files or symlinks") => skip_entry(),
other => other?,
} Prevention
- Check entry kind before hashing
- Handle submodule paths via their commit ids
- Re-stat paths after branch switches to avoid races
- Exclude special files (fifos/sockets) from change computation
When it happens
Trigger: Calling merged_change_is_effective (or the hashing helper) on a path that resolves to a directory at hash time — commonly because the file was deleted and replaced by a directory, or the path is a git submodule/fifo/socket.
Common situations: A branch swap where a formerly-tracked file path became a directory; submodule checkout state making a path a directory; race where the file is replaced between stat and hash; special files on Linux.
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
- `but setup` cannot run from a linked worktree; run it from…
- Cannot currently work in repositories without a worktree
- Cannot currently work in repositories without a worktree
- Cannot restore branch
- Deletions or additions aren't well-defined for hunk-based…
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/b9686648cd8212a1.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-core/src/diff/worktree.rs:988
&mut stream,
md.len(),
&mut gix::progress::Discard,
&gix::interrupt::IS_INTERRUPTED,
)?,
ToGitOutcome::Process(mut stream) => {
let mut buf = repo.empty_reusable_buffer();
stream.read_to_end(&mut buf)?;
gix::objs::compute_hash(repo.object_hash(), gix::object::Kind::Blob, &buf)?
}
ToGitOutcome::Buffer(buf) => {
gix::objs::compute_hash(repo.object_hash(), gix::object::Kind::Blob, buf)?
}
}
} else if md.is_symlink() {
let bytes = gix::path::os_string_into_bstring(std::fs::read_link(path)?.into())?;
gix::objs::compute_hash(repo.object_hash(), gix::object::Kind::Blob, &bytes)?
} else {
bail!("Cannot hash directory entries that aren't files or symlinks");
};
Ok(id)
}
fn into_tree_entry_kind(mode: gix::index::entry::Mode) -> anyhow::Result<EntryKind> {
Ok(mode
.to_tree_entry_mode()
.with_context(|| format!("Entry contained invalid entry mode: {mode:?}"))?
.kind())
}
/// Most importantly, this function allows to skip over untrackable entries, like named pipes, sockets and character devices, just like Git.
/// `path` is needed for now while we have to stat the file again to learn about the executable bits.
// TODO: remove `path` and provide the stat information or at least executable info with `gitoxide` - it has that info.
fn disk_kind_to_entry_kind(
disk_kind: Option<gix::dir::entry::Kind>,
index_kind: Option<gix::dir::entry::Kind>,
path: PathBuf,View on GitHub (pinned to 58e5313667)