GitoxideLabs/gitoxide · error
could not read tix
Error message
could not read tix {label}: {err} What it means
Tix stores its own state (e.g. review and pin entries) under prefixed refs. When enumerating these tix refs, a failure to read a specific reference that is not a benign missing-ref condition is wrapped as `could not read tix {label}: {err}`, where `label` identifies the ref family (e.g. review, pin).
Solutions
- Inspect and repair refs under the tix prefix (e.g. `refs/tix/...`) — remove corrupt loose ref files or stale `.lock` files
- Run `git fsck` to confirm ref integrity
- Fix filesystem permissions on the tix ref paths
- Delete the broken tix state ref and recreate it via the corresponding tix command (e.g. re-create the review)
Example fix
// recovery (shell) — remove a stale lock blocking a tix ref rm .git/refs/tix/review/0001.lock # or repair a corrupt loose ref by rewriting the object id echo <valid-object-id> > .git/refs/tix/review/0001
Defensive patterns
Strategy: try-catch
Validate before calling
// verify tix state refs exist and are readable before loading
for prefix in ["refs/tix/review/", "refs/tix/pin/"] {
let out = std::process::Command::new("git")
.args(["for-each-ref", prefix, "--format=%(refname)"])
.output()
.expect("run git");
if !out.status.success() {
eprintln!("tix refs under {prefix} unreadable");
}
} Type guard
fn is_benign_tix_ref_error(err: &dyn std::error::Error) -> bool {
err.to_string().to_lowercase().contains("not found")
} Try / catch
match load_tix_refs(&repo, "review") {
Ok(refs) => refs,
Err(e) if e.to_string().starts_with("could not read tix") => {
eprintln!("tix state ref corrupt: {e}; rebuilding state");
rebuild_tix_state(&repo)?
}
Err(e) => return Err(e),
} Prevention
- Never edit refs under `refs/tix/` by hand
- Clear leftover `.lock` files after a tix crash
- Back up tix state refs before bulk ref operations
- Run tix commands from a filesystem with stable permissions
When it happens
Trigger: Calling the tix-ref enumeration API (prefixed refs iteration, history.rs:1488) when a ref under the tix prefix (REVIEW_PREFIX or similar) fails to load with an error other than missing-ref — corrupt loose ref file, permission denied, or packed-refs damage under `refs/tix/...`.
Common situations: Manual deletion or editing of refs under `refs/tix/`, leftover lock files from a crashed tix run, filesystem permission changes, or interrupted writes to tix state refs.
Understand the failure class
Background: "git command failed": what it means when a tool shells out to git and git exits non-zero — this error's family across 21 libraries.
Related errors
- change ID prefix is ambiguous in the default Tix view
- show requires at least one -x/--hide revision when no…
- path does not name a file
- the new commit would be empty; use --allow-empty to create…
- rebase todo requires at least one -x/--hide revision when…
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/88a2a88c4bf141aa.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/history.rs:1488
}
pub(crate) fn review_number(name: &BStr) -> Option<&BStr> {
let suffix = name.strip_prefix(REVIEW_PREFIX)?;
(suffix.first().is_some_and(|digit| matches!(digit, b'1'..=b'9')) && suffix.iter().all(u8::is_ascii_digit))
.then_some(suffix.as_bstr())
}
fn refs_with_commit_targets(repo: &gix::Repository, prefix: &[u8], label: &str) -> Result<Vec<Pin>> {
let mut out = Vec::new();
let references = repo.references().context("could not open references")?;
for reference in references
.prefixed(prefix.as_bstr())
.with_context(|| format!("could not iterate tix {label}s"))?
{
let mut reference = match reference {
Ok(reference) => reference,
Err(err) if is_missing_ref(&*err) => continue,
Err(err) => return Err(anyhow::anyhow!("could not read tix {label}: {err}")),
};
let suffix = reference.name().as_bstr().strip_prefix(prefix).unwrap_or_default();
let valid_suffix = if prefix == REVIEW_PREFIX {
review_number(reference.name().as_bstr()).is_some()
} else {
suffix.len() >= 4 && suffix.iter().all(u8::is_ascii_alphanumeric)
};
if !valid_suffix {
tracing::warn!(name = %reference.name(), %label, "ignoring malformed tix reference");
continue;
}
let name = reference.name().to_owned();
let target = reference.target().into_owned();
if let Some(target_name) = target.try_name()
&& crate::edit::undo::ref_chain_reaches_queue(repo, target_name)?
{
tracing::warn!(name = %name, %label, "ignoring tix reference into the undo queue");
continue;View on GitHub (pinned to e73179060b)