abhigyanpatwari/GitNexus · error · InvalidBranchError

: branch name must not be "HEAD".

Error message

${source}: branch name must not be "HEAD".

What it means

validateBranchName rejects the literal string 'HEAD' (case-sensitive) because HEAD is git's symbolic ref for the current commit, not a real branch name. Using it as a branch argument would make the library operate on an alias rather than a concrete branch, so it is refused up front. Lower-case 'head' is a legal branch name and is allowed.

Solutions

  1. Pass the actual branch name (e.g. read it with `git rev-parse --abbrev-ref HEAD`) instead of the literal 'HEAD'.
  2. If you mean 'whatever is checked out', use the library's default-branch/current-commit option rather than naming HEAD.
  3. For lower-case 'head', no error occurs — verify you did not accidentally capitalize the branch name.

Example fix

// before
const branch = "HEAD";
validateBranchName(branch);
// after
const branch = execSync("git rev-parse --abbrev-ref HEAD").toString().trim(); // e.g. "main"
validateBranchName(branch);
Defensive patterns

Strategy: validation

Validate before calling

if (typeof branch === "string" && branch === "HEAD") {
  branch = execSync("git rev-parse --abbrev-ref HEAD").toString().trim();
}

Type guard

function isConcreteBranch(v: unknown): v is string {
  return typeof v === "string" && v !== "HEAD" && v.trim().length > 0;
}

Try / catch

try {
  validateBranchName(branch, "cli");
} catch (e) {
  if (e instanceof InvalidBranchError && e.message.includes('"HEAD"')) {
    branch = execSync("git rev-parse --abbrev-ref HEAD").toString().trim();
    validateBranchName(branch, "cli");
  } else throw e;
}

Prevention

When it happens

Trigger: Calling validateBranchName with branch='HEAD', e.g. `gitnexus impact --branch HEAD`, or an HTTP call with {"branch":"HEAD"}.

Common situations: Scripts defaulting to `$(git symbolic-ref HEAD)` or the literal 'HEAD'; CI configs passing a HEAD placeholder where the checked-out branch name is expected; users confusing the current-commit alias with a branch.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-09-15). Data as JSON: /api/errors/d5ab9b11827d0530. Report an issue: GitHub.

Appendix: source

Thrown at gitnexus/src/core/git-ref.ts:86

  // git ref-name rules (subset): reject characters git itself forbids in refs.
  if (/[~^:?*[\\]/.test(trimmed)) {
    throw new InvalidBranchError(
      `${source}: branch name contains characters not allowed in a git ref (~ ^ : ? * [ \\).`,
    );
  }
  if (trimmed.startsWith('-')) {
    throw new InvalidBranchError(`${source}: branch name must not start with "-".`);
  }
  // Force-refspec prefix (`git fetch origin +main` / `+refs/heads/main:…`).
  // Rejected here so neither the CLI nor HTTP can pass a force-update refspec
  // through as a "branch" (#3199 review, defense in depth).
  if (trimmed.startsWith('+')) {
    throw new InvalidBranchError(`${source}: branch name must not start with "+".`);
  }
  // The symbolic ref HEAD (case-sensitive). A repo can have a branch named
  // `head`; git itself treats only `HEAD` as the current-commit alias.
  if (trimmed === 'HEAD') {
    throw new InvalidBranchError(`${source}: branch name must not be "HEAD".`);
  }
  if (trimmed.includes('..')) {
    throw new InvalidBranchError(`${source}: branch name must not contain "..".`);
  }
  // The remaining `git check-ref-format` rules. Without these the validator
  // accepted refs git itself refuses (`feature.lock`, `/feature`, `feature/`,
  // `feature//next`, `@`, `.hidden`), so the failure surfaced later from the
  // git subprocess instead of here. No real branch can violate them — git
  // could not have created one — so nothing that works today starts failing.
  if (trimmed.endsWith('.lock') || trimmed.split('/').some((part) => part.endsWith('.lock'))) {
    throw new InvalidBranchError(`${source}: branch name must not end with ".lock".`);
  }
  if (trimmed.startsWith('/') || trimmed.endsWith('/')) {
    throw new InvalidBranchError(`${source}: branch name must not start or end with "/".`);
  }
  if (trimmed.includes('//')) {
    throw new InvalidBranchError(`${source}: branch name must not contain consecutive slashes.`);
  }

View on GitHub (pinned to ac9a4e9abd)