zed-industries/zed · error
expected commit object, got {object_type}
Error message
expected commit object, got {object_type} What it means
cat-file --batch resolved the requested SHA, but the object's type is not `commit` — it is a blob, tree, or annotated tag. The commit-loading path only accepts commit objects, so it refuses with the actual type name in the message.
Source
Thrown at crates/git/src/repository.rs:3583
))
}
.boxed()
}
fn commit_data_reader(&self) -> Result<CommitDataReader> {
let git_binary = self.git_binary();
let (request_tx, request_rx) = async_channel::bounded::<CommitDataRequest>(64);
let task = self.executor.spawn(async move {
if let Err(error) = run_commit_data_reader(git_binary, request_rx).await {
log::error!("commit data reader failed: {error:?}");
}
});
Ok(CommitDataReader {
request_tx,
_task: task,
})
}
fn set_trusted(&self, trusted: bool) {
self.is_trusted
.store(trusted, std::sync::atomic::Ordering::Release);
}
fn is_trusted(&self) -> bool {
self.is_trusted.load(std::sync::atomic::Ordering::Acquire)
}
}
async fn run_commit_data_reader(
git: GitBinary,
request_rx: async_channel::Receiver<CommitDataRequest>,
) -> Result<()> {
let mut process = gitView on GitHub (pinned to 5a9b9558db)
Solutions
- Dereference to a commit before loading: resolve with `git rev-parse <spec>^{commit}` and use the resulting SHA.
- Check the type first: `git cat-file -t <oid>` and skip non-commit objects.
- When capturing SHAs to pass around, always capture commit OIDs (from `rev-parse HEAD`-style calls, never tree or blob OIDs).
- Treat tag objects as expected input and peel them (`^{}` / `^{commit}`) rather than erroring.
Example fix
// before
let commit_sha = tree_oid_captured_from_ls_tree;
// after
let commit_sha = git
.run(&["rev-parse", &format!("{spec}^{{commit}}")])
.await?
.trim()
.to_string(); Defensive patterns
Strategy: type-guard
Validate before calling
// peel any spec to a commit OID before the commit-loading call
let commit_sha = git
.run(&["rev-parse", &format!("{spec}^{{commit}}")])
.await?;
let commit_sha = commit_sha.trim(); Type guard
async fn resolves_to_commit(git: &GitBinary, spec: &str) -> bool {
git.run(&["cat-file", "-t", spec]).await
.map(|t| t.trim() == "commit")
.unwrap_or(false)
} Try / catch
match load_commits(&shas).await {
Err(e) if e.to_string().contains("expected commit object") => {
// peel with ^{commit} and retry, or drop the non-commit OIDs
}
other => other?,
} Prevention
- Always peel refs and tags with `^{commit}` before commit lookups.
- Store only commit OIDs when persisting references for later commit loads.
- Check `git cat-file -t` on OIDs of mixed provenance (index, ls-tree, blame) before use.
When it happens
Trigger: Passing a blob OID (for example a file blob SHA captured from blame or the index) or a tree OID (from `git ls-tree` / `rev-parse HEAD^{tree}`) where a commit SHA is expected; passing an annotated-tag OID without dereferencing it to the commit it points at.
Common situations: OIDs collected from mixed git commands fed into a commit-metadata lookup; short SHAs resolving to an unexpected object in tag-heavy repositories; storing `git rev-parse <tag>` output (tag object) instead of `<tag>^{commit}`.
Related errors
- unexpected number of shas
- revision spec {revision:?} contains a newline and cannot be
- invalid cat-file header: {header_line}
- object not found: {}
- grammar directory '{}' already exists, but is not a git clon
AI-assisted analysis of zed-industries/zed@5a9b9558db (2026-08-20).
Data as JSON: /api/errors/5131d1ea289a4145.
Report an issue: GitHub.