abhigyanpatwari/GitNexus · error · InvalidBranchError

: branch name contains characters not allowed in a git ref…

Error message

${source}: branch name contains characters not allowed in a git ref (~ ^ : ? * [ \).

What it means

validateBranchName enforces a subset of git's ref-name rules and throws InvalidBranchError '<source>: branch name contains characters not allowed in a git ref (~ ^ : ? * [ \\).' when the name matches /[~^:?*[\\]/. Git itself forbids these characters in refs, so any such name would fail at git level anyway; the library rejects it early with a clear message.

Solutions

  1. Remove or replace the forbidden characters (~ ^ : ? * [ \\) with safe ones such as '-' or '/'.
  2. Convert path separators to '/' (or '-') when deriving names from paths: value.replace(/\\+/g, '-').
  3. Sanitize generated names (e.g. timestamps '2026-09-15T10:00:00Z' → '2026-09-15-100000z') before validation.
  4. Adopt a naming convention of lowercase alphanumerics, '-', '_', and '/' only.

Example fix

// before
const branch = `work/${new Date().toISOString()}`; // contains ':'
validateBranchName(branch, 'worktree');
// after
const branch = `work/${new Date().toISOString().replace(/[:]/g, '-')}`;
validateBranchName(branch, 'worktree');
Defensive patterns

Strategy: validation

Validate before calling

if (/[~^:?*[\\]/.test(name)) {
  name = name.replace(/[~^:?*[\\]/g, '-');
}

Type guard

const isGitRefSafe = (v: string): boolean => !/[~^:?*[\\]/.test(v);

Try / catch

try {
  branch = validateBranchName(input, 'worktree');
} catch (e) {
  if (e instanceof InvalidBranchError && /not allowed in a git ref/.test(e.message)) {
    branch = validateBranchName(input.replace(/[~^:?*[\\]/g, '-'), 'worktree');
  } else throw e;
}

Prevention

When it happens

Trigger: Calling validateBranchName with any of ~ ^ : ? * [ or \\ in the value — e.g. 'feature:v2', 'release~1', 'fix*test', a Windows path fragment like 'feature\\foo', or glob-like names 'bug?[0-9]'.

Common situations: Deriving branch names from file paths on Windows (backslashes); using colon-separated prefixes like 'user:topic'; wildcard patterns intended for matching being passed as literal names; date strings with colons from toISOString-style formatting.

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/7717b21f03aa03ae. Report an issue: GitHub.

Appendix: source

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

 * Validate a user-supplied branch name. Returns the trimmed name or throws
 * {@link InvalidBranchError}. Conservative but accepts the shapes real
 * branches use (`feature/foo-bar`, `release/1.2`, `develop`).
 */
export function validateBranchName(value: string, source: string): string {
  const trimmed = value.trim();
  if (!trimmed) {
    throw new InvalidBranchError(`${source}: branch name must not be empty.`);
  }
  if (trimmed.length > BRANCH_MAX_LENGTH) {
    throw new InvalidBranchError(`${source}: branch name is too long (max ${BRANCH_MAX_LENGTH}).`);
  }
  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('..')) {

View on GitHub (pinned to ac9a4e9abd)