JuliusBrussee/caveman · error · Error

base and head must be full Git object IDs

Error message

base and head must be full Git object IDs

What it means

checkProfileScope requires base and head to be full Git object IDs (40-char hex SHAs) validated by REVISION_RE, and throws 'base and head must be full Git object IDs' otherwise. The gate deliberately rejects symbolic refs, short SHAs, and branch names so its cat-file/diff plumbing commands operate on unambiguous commits.

Solutions

  1. Resolve refs to full SHAs before calling: git rev-parse <ref>^{commit}
  2. In CI, use the event's commit SHA variables (e.g. GITHUB_SHA, or actions/checkout's fetch-depth + rev-parse) instead of branch names
  3. Reject/normalize short SHAs by expanding them with git rev-parse
  4. Validate input length (40 hex chars, 64 for SHA-256 repos) before invoking the gate

Example fix

// before
checkProfileScope({ base: 'main', head: 'HEAD' })
// after
const base = exec('git rev-parse main^{commit}').trim();
const head = exec('git rev-parse HEAD^{commit}').trim();
checkProfileScope({ base, head })
Defensive patterns

Strategy: validation

Validate before calling

const REVISION_RE = /^[0-9a-f]{40}$|^[0-9a-f]{64}$/;
if (!REVISION_RE.test(base) || !REVISION_RE.test(head)) {
  throw new Error(`base/head must be full object IDs, got base=${base} head=${head}`);
}

Type guard

const isFullOid = (s) => typeof s === 'string' && /^[0-9a-f]{40}$|^[0-9a-f]{64}$/.test(s);

Try / catch

try { checkProfileScope({ base, head }); } catch (e) {
  if (e.message.includes('full Git object IDs')) {
    base = revParse(base); head = revParse(head); // resolve refs to SHAs and retry
    return checkProfileScope({ base, head });
  } throw e;
}

Prevention

When it happens

Trigger: Calling checkProfileScope({ base, head }) with branch names ('main'), short SHAs ('abc123'), ranges ('main...head'), annotated ref names, or refs like 'HEAD~1'.

Common situations: CI systems exporting branch names instead of commit SHAs into base/head variables, GitHub Actions events providing refs rather than SHAs, or hand-testing with abbreviated hashes.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/ac5085e6b5b8f06a. Report an issue: GitHub.

Appendix: source

Thrown at agents/check-profile-scope.mjs:46

}

function requireGit(result, operation) {
  if (!result.error && result.status === 0) return result.stdout;
  const detail = result.stderr?.toString().trim() || result.error?.message || `exit ${result.status}`;
  throw new Error(`${operation} failed: ${detail}`);
}

function isConcreteProfile(path) {
  return path !== SCHEMA_PATH && /^agents\/profiles\/[a-z0-9][a-z0-9-]*\.json$/.test(path);
}

function isAllowedConcreteProfileCommitPath(path) {
  return isConcreteProfile(path) || GENERATED_PATHS.has(path);
}

export function checkProfileScope({ base, head, cwd = process.cwd() }) {
  if (!REVISION_RE.test(base) || !REVISION_RE.test(head)) {
    throw new Error("base and head must be full Git object IDs");
  }

  const baseRegistry = runGit(cwd, ["cat-file", "-e", `${base}:agents/agents.json`]);
  if (baseRegistry.error || baseRegistry.status !== 0) {
    return { ok: true, skipped: true, message: "profile registry absent at base (initial import); skipping lane-scope gate" };
  }

  const commitsText = requireGit(runGit(cwd, ["rev-list", "--reverse", `${base}..${head}`]), "git rev-list");
  const commits = commitsText.split(/\r?\n/).filter(Boolean);
  let contractTouched = false;

  for (const commit of commits) {
    const changedBuffer = requireGit(
      // -m takes the union across merge parents; otherwise a merge commit can hide
      // an out-of-scope path from an ordinary single-parent diff-tree view.
      runGit(cwd, ["diff-tree", "--root", "-m", "--no-commit-id", "--name-only", "-r", "-z", commit], null),
      `git diff-tree ${commit}`,
    );

View on GitHub (pinned to 3ee70a1026)