can1357/oh-my-pi · error · RuntimeError

git cat-file returned {len(lines)} lines, expected {len(CACH

Error message

git cat-file returned {len(lines)} lines, expected {len(CACHE_KEY_PATHS)}: {proc.stdout!r}

What it means

`compute_key()` builds a deterministic sha256 cache key by piping `HEAD:<path>` requests for every entry of CACHE_KEY_PATHS ('crates', 'Cargo.lock', 'Cargo.toml', 'rust-toolchain.toml', 'packages/natives') into one `git cat-file --batch-check` invocation. It asserts the batch output contains exactly one line per requested path; if git returns fewer (or more) lines, the parse would silently misalign path-to-hash pairs, so a RuntimeError is raised with the raw stdout for diagnosis.

Source

Thrown at python/robomp/src/natives_cache.py:159

    Raises ``subprocess.CalledProcessError`` if ``git`` itself fails (e.g.
    not a repo) — callers SHOULD treat that as "no cache" and proceed.
    """
    tgt = target if target is not None else target_triple()
    stdin = "".join(f"HEAD:{p}\n" for p in CACHE_KEY_PATHS)
    proc = subprocess.run(
        ["git", "cat-file", "--batch-check"],
        input=stdin,
        cwd=str(repo_dir),
        text=True,
        capture_output=True,
        check=True,
        env=_git_safe_directory_env(repo_dir),
        timeout=120.0,
    )
    lines = proc.stdout.splitlines()
    if len(lines) != len(CACHE_KEY_PATHS):
        raise RuntimeError(
            f"git cat-file returned {len(lines)} lines, expected {len(CACHE_KEY_PATHS)}: {proc.stdout!r}"
        )
    h = hashlib.sha256()
    for path, line in zip(CACHE_KEY_PATHS, lines, strict=True):
        stripped = line.strip()
        if stripped.endswith("missing"):
            tree_hash = _NULL_TREE_HASH
        else:
            # "<hash> <type> <size>" — take the first token as the tree/blob hash.
            tree_hash = stripped.split(None, 1)[0]
        h.update(f"{path}\t{tree_hash}\n".encode())
    h.update(f"TARGET\t{tgt}\n".encode())
    return h.hexdigest()


def _repo_slug(repo: str) -> str:
    """Same convention as ``SandboxManager.pool_path``."""
    return repo.replace("/", "__")

View on GitHub (pinned to 9690622007)

Solutions

  1. Inspect the `proc.stdout` echoed in the message to see which lines came back and which path line is missing or extra
  2. Reproduce manually in the repo: `printf 'HEAD:crates\nHEAD:Cargo.lock\nHEAD:Cargo.toml\nHEAD:rust-toolchain.toml\nHEAD:packages/natives\n' | git cat-file --batch-check` and compare line count to 5
  3. Check the repo is a normal full clone with the expected monorepo layout; re-clone without --filter/--sparse if needed
  4. Verify the git version matches the one used in CI/Docker (the pi image pins its toolchain)
  5. Catch the RuntimeError at the call site and treat it as 'no cache', proceeding with a normal build — the documented fallback posture for compute_key failures

Example fix

# before
key = compute_key(workspace_repo)
entry = cache.lookup(repo, key)

# after
try:
    key = compute_key(workspace_repo)
except (RuntimeError, subprocess.CalledProcessError, subprocess.TimeoutExpired):
    key = None  # no cache; proceed with a normal build
entry = cache.lookup(repo, key) if key else None
Defensive patterns

Strategy: try-catch

Validate before calling

def repo_supports_cache_key(repo_dir):
    # Missing paths are tolerated (null hash); the guard is that git works and the layout matches
    out = subprocess.run(["git", "rev-parse", "--is-inside-work-tree"], cwd=repo_dir, capture_output=True)
    return out.returncode == 0 and not (Path(repo_dir) / ".git").is_file()  # avoid sparse/worktree edge cases

Type guard

def cat_file_output_well_formed(stdout: str, expected: int) -> bool:
    return len(stdout.splitlines()) == expected

Try / catch

try:
    key = compute_key(repo_dir)
except (RuntimeError, subprocess.CalledProcessError, subprocess.TimeoutExpired):
    key = None  # treat as cache miss and do a normal build

Prevention

When it happens

Trigger: Calling `compute_key(repo_dir)` (directly or via the NativesCache capture/populate flow) where `git cat-file --batch-check` does not emit exactly len(CACHE_KEY_PATHS)=5 lines: git aborts writing on one line early, a sparse/partial clone suppresses object lines, a git hook/config/filter alters batch-check output, or a fake git stub in tests emits truncated stdout. Called by tests like test_compute_key_deterministic_across_clones and test_capture_then_populate_shares_node_inode_but_copies_companions.

Common situations: Running against a shallow or partial (blobless) clone; a sparse-checkout worktree where `HEAD:crates` resolves unexpectedly; an old or patched git version in the runtime image whose batch-check formatting differs; a replace-ref or filter config changing output; a race where HEAD changes mid-invocation.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/0e8b3102d0be49a3. Report an issue: GitHub.