gitbutlerapp/gitbutler · error · anyhow::Error
Can't handle reference '{rn}' of category '{category:?}'
Error message
Can't handle reference '{rn}' of category '{category:?}' What it means
Thrown by the anonymization walk in but-graph's debug module (crates/but-graph/src/debug.rs:95). When producing anonymized debug output (e.g., `but graph debug` visualizations), every reference name in the graph is rewritten to a stable fake name; the match on Category simply has no anonymization arm for Note, PseudoRef, MainPseudoRef, MainRef, LinkedPseudoRef, LinkedRef, Bisect, Rewritten, or WorktreePrivate refs. Hitting it means the repository graph contains one of those ref kinds and the anonymizer chose to fail loudly rather than leak or mangle the real name.
Source
Thrown at crates/but-graph/src/debug.rs:95
let new_short_name = name_mapping
.entry(short_name.to_owned())
.or_insert_with(|| int_to_alpha(num_names).into());
new_name.push_byte(b'/');
new_name.push_str(new_short_name);
*rn = gix::refs::FullName::try_from(new_name.as_bstr())
.expect("Our replacement names are always valid");
}
Category::Note
| Category::PseudoRef
| Category::MainPseudoRef
| Category::MainRef
| Category::LinkedPseudoRef { .. }
| Category::LinkedRef { .. }
| Category::Bisect
| Category::Rewritten
| Category::WorktreePrivate => {
bail!("Can't handle reference '{rn}' of category '{category:?}'");
}
}
Ok(())
};
for node in self.inner.node_weights_mut() {
if let Some(ri) = node.ref_info.as_mut() {
anon(&mut ri.ref_name)?;
}
if let Some(rn) = node.remote_tracking_ref_name.as_mut() {
anon(rn)?;
}
for ri in node.commits.iter_mut().flat_map(|c| c.refs.iter_mut()) {
anon(&mut ri.ref_name)?;
}
if let Some(SegmentMetadata::Workspace(md)) = node.metadata.as_mut() {
for rn in md
.stacks
.iter_mut()View on GitHub (pinned to caf1f223d3)
Solutions
- Inspect which ref triggered it (the message names ref and category): `git for-each-ref --format='%(refname)' | grep -E 'notes|bisect|rewritten'`
- Clean the transient state: finish or reset `git bisect reset`, complete the rebase, drop stale refs/rewritten and refs/notes entries you do not need, then rerun
- If you need anonymized output despite those refs, extend the match in debug.rs with mapping arms for the listed categories
- Otherwise use the non-anonymized debug output for that repo
Example fix
// before (debug.rs) — categories with no anonymization arm bail
Category::Note | Category::Bisect | Category::Rewritten | Category::WorktreePrivate => {
bail!("Can't handle reference '{rn}' of category '{category:?}'");
}
// after — map them to a category-tagged placeholder instead of failing
Category::Note => {
*rn = gix::refs::FullName::try_from(format!("refs/anon-notes/{}", counter.next()))
.expect("generated name is valid");
}
Category::Bisect | Category::Rewritten | Category::WorktreePrivate
| Category::PseudoRef | Category::MainPseudoRef | Category::MainRef
| Category::LinkedPseudoRef { .. } | Category::LinkedRef { .. } => {
*rn = gix::refs::FullName::try_from(format!("refs/anon/{category:?}/{}", counter.next()))
.expect("generated name is valid");
} Defensive patterns
Strategy: fallback
Validate before calling
// Pre-filter refs the anonymizer cannot handle before generating debug output
use but_graph::Category;
fn anonymizable(name: &gix::refs::FullName) -> bool {
!matches!(
name.category(),
Some(Category::Note)
| Some(Category::PseudoRef)
| Some(Category::MainPseudoRef)
| Some(Category::MainRef)
| Some(Category::LinkedPseudoRef { .. })
| Some(Category::LinkedRef { .. })
| Some(Category::Bisect)
| Some(Category::Rewritten)
| Some(Category::WorktreePrivate)
)
} Try / catch
match graph_debug.anonymize() { // conceptual
Ok(out) => out,
Err(err) if err.to_string().contains("Can't handle reference") => {
tracing::warn!("repo has non-anonymizable refs (notes/bisect/rewritten/worktree); emitting non-anonymized output");
graph_debug.raw()
}
Err(err) => return Err(err),
} Prevention
- Run `git bisect reset` and finish rebases before capturing anonymized graph dumps
- Prune refs/notes, refs/rewritten, and stale worktree refs you do not need before debugging
When it happens
Trigger: Running graph debug/anonymization on a repository that has refs/notes/*, refs/bisect/* (mid git bisect), refs/rewritten/* (from rebase --update-refs), worktree-private refs (per-worktree HEAD/bisect state), or pseudo-refs like FETCH_HEAD/MERGE_REF at non-standard names; linked worktrees with their own branch namespaces.
Common situations: Debugging a graph dump right after a `git bisect` or `git rebase --update-refs` session; repos with git-notes enabled; running the anonymizer in CI fixtures that synthesize exotic refs; new but-graph versions adding categories without extending the debug anonymizer.
Related errors
- Cannot currently handle remotes as start position
- Failed to find reference {ref_name} due to it being dropped
- BUG: {id} is queued
- handled above
- prefiltered
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/71bdd8e6481248ce.
Report an issue: GitHub.