ruvnet/ruflo · error · Error

malformed Git index record

Error message

malformed Git index record

What it means

submoduleStates() parses the output of `git ls-files --stage -z`; each record must contain a tab (0x09) separating '<mode> <sha> <stage>' metadata from the path. A record with no tab means the output does not match the expected git format, so parsing aborts with 'malformed Git index record'.

Source

Thrown at v3/@claude-flow/codex/src/harness/repository-state.ts:302

  return { digest: digest(canonicalJson(entries)), entries };
}

function trackedPatch(repoRoot: string): ContentDigest {
  return contentDigest(gitBuffer(repoRoot, [
    'diff',
    '--binary',
    '--full-index',
    '--no-ext-diff',
    'HEAD',
    '--',
  ]));
}

function submoduleStates(repoRoot: string): readonly SubmoduleState[] {
  const staged = splitNul(gitBuffer(repoRoot, ['ls-files', '--stage', '-z']));
  const submodules = staged.flatMap((entry): Array<{ path: string; commit: string }> => {
    const tab = entry.indexOf(0x09);
    if (tab < 0) throw new Error('malformed Git index record');
    const metadata = entry.subarray(0, tab).toString('ascii');
    const match = /^160000 ([0-9a-f]+) [0-3]$/.exec(metadata);
    return match?.[1]
      ? [{ path: decodeGitPath(entry.subarray(tab + 1)), commit: match[1] }]
      : [];
  });
  assertNoPathCollisions(submodules.map(({ path }) => path));

  return submodules.map(({ path, commit }): SubmoduleState => {
    const absolute = resolve(repoRoot, path);
    assertInsideRepository(repoRoot, absolute);
    const actualCommit = optionalGitText(absolute, ['rev-parse', '--verify', 'HEAD']);
    if (!actualCommit) {
      return {
        path,
        initialized: false,
        expectedCommit: commit,
        trackedPatch: contentDigest(Buffer.alloc(0)),

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Run `git ls-files --stage` manually and inspect the raw output for records without a tab
  2. Rebuild the index: `rm .git/index && git reset`
  3. Upgrade git to a current stable release and remove any git shims/aliases from PATH
Defensive patterns

Strategy: try-catch

Validate before calling

const records = execFileSync('git', ['-C', root, 'ls-files', '--stage', '-z']);
for (const rec of splitNul(records)) if (!rec.includes(0x09)) throw new Error('unexpected git output shape');

Try / catch

try { capture(); } catch (e) { if (/malformed Git index record/.test(String(e))) { rebuildIndexOrUpgradeGit(); } throw e; }

Prevention

When it happens

Trigger: A git version or wrapper that emits `ls-files --stage` output in a different shape; a corrupted index producing garbage records; output mangled by GIT_EXTERNAL_DIFF/aliases or a shim in front of git.

Common situations: Very old or exotic git builds, git installed via wrappers (hub, containers, busybox git), or a .git/index corrupted by a crash — the regex/tab contract silently breaks.

Understand the failure class

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/cf0a184be0054697. Report an issue: GitHub.