abhigyanpatwari/GitNexus · error · InvalidBranchError

: branch name must not start with "+".

Error message

${source}: branch name must not start with "+".

What it means

validateBranchName rejects branch names starting with '-' to prevent the name being interpreted as a command-line option when passed to a git subprocess (option-injection defense). A name like '-oProxyCommand=...' would otherwise flow into `git checkout <branch>` style invocations as a flag.

Solutions

  1. Remove the leading dash from the branch value before calling the API.
  2. Check argument order in your CLI/script invocation — a missing flag value may have shifted a '-'-prefixed token into the branch slot.
  3. If the intent was a git option, it is not accepted here; set options via the library's dedicated parameters, never through the branch name.

Example fix

// before
const branch = process.argv[4]; // "--force"
validateBranchName(branch);
// after
const branch = process.argv[4];
if (branch) validateBranchName(branch.replace(/^-+/, "")); // "force"
Defensive patterns

Strategy: validation

Validate before calling

function startsWithDash(name) { return typeof name === "string" && name.startsWith("-"); }
if (startsWithDash(branch)) throw new Error("branch name must not start with '-'");

Type guard

function isOptionSafeBranch(v: unknown): v is string {
  return typeof v === "string" && !v.trim().startsWith("-");
}

Try / catch

try {
  validateBranchName(branch, "http");
} catch (e) {
  if (e instanceof InvalidBranchError && e.message.includes('start with "-"')) {
    return res.status(400).json({ error: "branch names must not start with '-'" });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling validateBranchName with a value like '-feature', '--depth=1', or a user/HTTP-supplied branch argument that begins with a dash, e.g. `gitnexus analyze --branch -foo`.

Common situations: Hand-crafted HTTP requests hitting a /branch endpoint with a dash-prefixed value; CLI scripts interpolating flags into a branch variable; typo where a flag value was omitted so the next flag is consumed as the branch name.

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/645ef0b2124832cc. Report an issue: GitHub.

Appendix: source

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

  }
  assertNoHiddenChars(trimmed, source);
  if (/\s/.test(trimmed)) {
    throw new InvalidBranchError(`${source}: branch name must not contain whitespace.`);
  }
  // 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('/')) {

View on GitHub (pinned to ac9a4e9abd)