{"record":{"id":"3c212d0512963386","repo":"abhigyanpatwari/GitNexus","slug":"source-branch-name-must-not-start-or-end-with","errorCode":null,"errorMessage":"${source}: branch name must not start or end with \"/\".","messagePattern":"(.+?): branch name must not start or end with \"/\"\\.","errorType":"validation","errorClass":"InvalidBranchError","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/git-ref.ts","lineNumber":100,"sourceCode":"  }\n  // The symbolic ref HEAD (case-sensitive). A repo can have a branch named\n  // `head`; git itself treats only `HEAD` as the current-commit alias.\n  if (trimmed === 'HEAD') {\n    throw new InvalidBranchError(`${source}: branch name must not be \"HEAD\".`);\n  }\n  if (trimmed.includes('..')) {\n    throw new InvalidBranchError(`${source}: branch name must not contain \"..\".`);\n  }\n  // The remaining `git check-ref-format` rules. Without these the validator\n  // accepted refs git itself refuses (`feature.lock`, `/feature`, `feature/`,\n  // `feature//next`, `@`, `.hidden`), so the failure surfaced later from the\n  // git subprocess instead of here. No real branch can violate them — git\n  // could not have created one — so nothing that works today starts failing.\n  if (trimmed.endsWith('.lock') || trimmed.split('/').some((part) => part.endsWith('.lock'))) {\n    throw new InvalidBranchError(`${source}: branch name must not end with \".lock\".`);\n  }\n  if (trimmed.startsWith('/') || trimmed.endsWith('/')) {\n    throw new InvalidBranchError(`${source}: branch name must not start or end with \"/\".`);\n  }\n  if (trimmed.includes('//')) {\n    throw new InvalidBranchError(`${source}: branch name must not contain consecutive slashes.`);\n  }\n  if (trimmed === '@') {\n    throw new InvalidBranchError(`${source}: branch name must not be the single character \"@\".`);\n  }\n  if (trimmed.includes('@{')) {\n    throw new InvalidBranchError(`${source}: branch name must not contain \"@{\".`);\n  }\n  if (trimmed.endsWith('.') || trimmed.split('/').some((part) => part.startsWith('.'))) {\n    throw new InvalidBranchError(\n      `${source}: branch name must not end with \".\" or have a path component starting with \".\".`,\n    );\n  }\n  // Git permits a backtick in a ref, but the branch is embedded inside a\n  // Markdown inline-code span in the generated AGENTS.md/CLAUDE.md regression\n  // example, where a backtick would close the span early and let the rest of","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/ac9a4e9abd8fd3058c070b72c23402a4f887929a/gitnexus/src/core/git-ref.ts#L82-L118","documentation":"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.","triggerScenarios":"Calling validateBranchName with '/feature', 'feature/', or a value built by string concatenation like `/${env.BRANCH}` or `${branch}/`.","commonSituations":"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.","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."],"exampleFix":"// before\nconst branch = `${ns}/${name}`; // ns is \"\" -> \"/feature\"\nvalidateBranchName(branch);\n// after\nconst branch = [ns, name].filter(Boolean).join(\"/\"); // \"feature\"\nvalidateBranchName(branch);","handlingStrategy":"validation","validationCode":"function hasEdgeSlashes(name) {\n  return typeof name === \"string\" && (name.startsWith(\"/\") || name.endsWith(\"/\"));\n}\nbranch = String(branch ?? \"\").replace(/^\\/+|\\/+$/g, \"\");","typeGuard":"function isTrimmedRef(v: unknown): v is string {\n  return typeof v === \"string\" && v.length > 0 && !v.startsWith(\"/\") && !v.endsWith(\"/\");\n}","tryCatchPattern":"try {\n  validateBranchName(branch, \"cli\");\n} catch (e) {\n  if (e instanceof InvalidBranchError && e.message.includes('\"/\"')) {\n    branch = branch.replace(/^\\/+|\\/+$/g, \"\");\n    validateBranchName(branch, \"cli\");\n  } else throw e;\n}","preventionTips":["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."],"tags":["git","branch-name","validation"],"backgroundTag":"invalid-identifier-format","analyzedSha":"ac9a4e9abd8fd3058c070b72c23402a4f887929a","analyzedAt":"2026-09-15T23:29:44.066Z","contentChangedAt":"2026-09-15T23:29:44.066Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}