abhigyanpatwari/GitNexus · error · InvalidBranchError
: branch name must not start or end with "/".
Error message
${source}: branch name must not start or end with "/". What it means
validateBranchName rejects names that begin or end with '/', mirroring git check-ref-format. A leading or trailing slash can never appear in a ref git created, and a leading slash is used to reject input early so the failure is a clear validation error rather than a git subprocess failure.
Solutions
- Strip leading/trailing slashes from the value before validation: `name.replace(/^\/+|\/+$/g, "")`.
- Fix the string-building code — typically an empty variable or a duplicated '/' in a template literal.
- Pass the short branch name ('feature/foo'), not a ref path with stray separators.
Example fix
// before
const branch = `${ns}/${name}`; // ns is "" -> "/feature"
validateBranchName(branch);
// after
const branch = [ns, name].filter(Boolean).join("/"); // "feature"
validateBranchName(branch); Defensive patterns
Strategy: validation
Validate before calling
function hasEdgeSlashes(name) {
return typeof name === "string" && (name.startsWith("/") || name.endsWith("/"));
}
branch = String(branch ?? "").replace(/^\/+|\/+$/g, ""); Type guard
function isTrimmedRef(v: unknown): v is string {
return typeof v === "string" && v.length > 0 && !v.startsWith("/") && !v.endsWith("/");
} Try / catch
try {
validateBranchName(branch, "cli");
} catch (e) {
if (e instanceof InvalidBranchError && e.message.includes('"/"')) {
branch = branch.replace(/^\/+|\/+$/g, "");
validateBranchName(branch, "cli");
} else throw e;
} Prevention
- Always trim slashes from interpolated values before building a branch argument.
- Use array filter(Boolean).join('/') for path-like joins so empty segments vanish.
- Reject empty env/config variables early — they are the usual cause of leading slashes.
- Pass short branch names, not ref paths, to branch parameters.
When it happens
Trigger: Calling validateBranchName with '/feature', 'feature/', or a value built by string concatenation like `/${env.BRANCH}` or `${branch}/`.
Common situations: Joining path segments with template strings (e.g. `` `${namespace}/${branch}` ``) where one side is empty; copying a full ref path 'refs/heads/main' variants; shell variable expansion producing empty prefix/suffix.
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 "HEAD".
AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-09-15).
Data as JSON: /api/errors/3c212d0512963386.
Report an issue: GitHub.
Appendix: source
Thrown at gitnexus/src/core/git-ref.ts:100
}
// 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.`);
}
if (trimmed === '@') {
throw new InvalidBranchError(`${source}: branch name must not be the single character "@".`);
}
if (trimmed.includes('@{')) {
throw new InvalidBranchError(`${source}: branch name must not contain "@{".`);
}
if (trimmed.endsWith('.') || trimmed.split('/').some((part) => part.startsWith('.'))) {
throw new InvalidBranchError(
`${source}: branch name must not end with "." or have a path component starting with ".".`,
);
}
// Git permits a backtick in a ref, but the branch is embedded inside a
// Markdown inline-code span in the generated AGENTS.md/CLAUDE.md regression
// example, where a backtick would close the span early and let the rest ofView on GitHub (pinned to ac9a4e9abd)