astrid-runtime/astrid · error

captured version chain must contain at least two readable ve

Error message

captured version chain must contain at least two readable versions

What it means

Version-chain corpora require at least two readable versions of a file to be meaningful for chunker comparison. After filtering revisions to those where the path actually exists and is readable, if fewer than two versions remain, `version_chain_from_git` bails with this error.

Source

Thrown at crates/astrid-storage-chunker-evidence/src/corpus.rs:122

                continue;
            }
            let object = format!("{revision}:{}", relative_path.to_string_lossy());
            let version = Command::new("git")
                .args(["-C"])
                .arg(repository)
                .args(["show", &object])
                .output()
                .context("read captured version")?;
            if !version.status.success() {
                bail!(
                    "git could not read captured version: {}",
                    String::from_utf8_lossy(&version.stderr).trim()
                );
            }
            inputs.push(memory(version.stdout));
        }
        if inputs.len() < 2 {
            bail!("captured version chain must contain at least two readable versions");
        }
        Self::from_files(name, CorpusKind::VersionChain, inputs)
    }

    pub fn synthetic_adversarial() -> Self {
        let mebibyte = 1024 * 1024;
        let inputs = vec![
            memory(Vec::new()),
            memory(vec![0x5a]),
            memory(vec![0x7e; 4093]),
            memory(vec![0; 8 * mebibyte]),
            memory(vec![0xff; 8 * mebibyte]),
            memory(periodic_bytes(8 * mebibyte)),
            memory((0_u8..=u8::MAX).cycle().take(8 * mebibyte).collect()),
            memory(b"abcd".repeat(2 * mebibyte)),
            memory(pseudorandom_bytes(8 * mebibyte, 0x5eed_f00d_dead_beef)),
            memory(boundary_pressure(8 * mebibyte)),
        ];

View on GitHub (pinned to affd8760f4)

Solutions

  1. Choose a file with a longer tracked history (at least two commits touching it).
  2. Verify renames didn't break history (`git log --follow <path>`) and use the original path if needed.
  3. Unshallow the clone so older revisions are available.
  4. Fall back to a synthetic corpus if the target file's history is too short.
Defensive patterns

Strategy: validation

Validate before calling

// Count reachable revisions touching the path before building the corpus
let out = Command::new("git").arg("-C").arg(&repo)
    .args(["rev-list", "--count", "HEAD", "--"]).arg(&path).output()?;
assert!(String::from_utf8_lossy(&out.stdout).trim().parse::<u32>().unwrap_or(0) >= 2,
    "path needs >= 2 revisions");

Try / catch

match version_chain_from_git(&repo, &path, name) {
    Err(e) if e.to_string().contains("at least two readable versions") => {
        // fall back to synthetic_adversarial() corpus or pick a longer-lived file
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling `version_chain_from_git` for a file whose history yields <2 readable versions: brand-new file with one commit, path with existence checks filtering out most revisions, or all `git show` reads failing.

Common situations: Pointing the corpus builder at a recently added file with a single commit; a file only present at HEAD after a rename history break; a shallow clone hiding older versions.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/b74fe10f1f15eb37. Report an issue: GitHub.