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
- Pass the actual branch name (e.g. read it with `git rev-parse --abbrev-ref HEAD`) instead of the literal 'HEAD'.
- If you mean 'whatever is checked out', use the library's default-branch/current-commit option rather than naming HEAD.
- 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
- Resolve HEAD to a concrete branch name before passing it as a branch parameter.
- In CI, capture the checked-out branch via GITHUB_REF_NAME / CI_COMMIT_REF_NAME rather than the literal 'HEAD'.
- Remember the check is case-sensitive: 'head' is valid, 'HEAD' is not.
- Prefer the library's default/current-commit option when you mean 'whatever is checked out'.
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
- err.message (InvalidBranchError rethrown as GitNexusRcError…
- : branch name contains characters not allowed in a git ref…
- : branch name is too long (max ).
- : branch name must not be empty.
- : branch name must not be the single character "@".
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)