{"record":{"id":"cc872cc375b4a310","repo":"abhigyanpatwari/GitNexus","slug":"source-branch-name-must-not-end-with-or-have-a-path","errorCode":null,"errorMessage":"${source}: branch name must not end with \".\" or have a path component starting with \".\".","messagePattern":"(.+?): branch name must not end with \"\\.\" or have a path component starting with \"\\.\"\\.","errorType":"validation","errorClass":"InvalidBranchError","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/git-ref.ts","lineNumber":112,"sourceCode":"  // 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\n  // the template render as instruction text. Reject it at this single\n  // chokepoint so all three tiers (CLI flag, .gitnexusrc, auto-detect via\n  // sanitizeDetectedBranch) are covered (#1996 tri-review P1).\n  if (trimmed.includes('`')) {\n    throw new InvalidBranchError(\n      `${source}: branch name must not contain a backtick (it would break the generated Markdown).`,\n    );\n  }\n  return trimmed;\n}\n","sourceCodeStart":94,"sourceCodeEnd":129,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/ac9a4e9abd8fd3058c070b72c23402a4f887929a/gitnexus/src/core/git-ref.ts#L94-L129","documentation":"validateBranchName rejects names ending with '.' or containing a '/'-separated component that starts with '.', matching git check-ref-format (git refuses refs like 'feature.' or '.hidden/foo'). The library throws early so hidden-file-style or trailing-dot names fail validation instead of inside the git subprocess.","triggerScenarios":"Calling validateBranchName with 'release.', 'v1.', '.hidden', '.github/workflows', or names built from filesystem paths whose segments start with a dot, e.g. concatenating a directory path into a branch name.","commonSituations":"Branch names derived from file paths (config generators that turn '.env/foo' into a branch); accidental trailing '.' from sentence-ending copy-paste ('branch named feature.'); template strings appending periods.","solutions":["Remove the trailing dot and any leading dots from path components: `name.split(\"/\").map(p => p.replace(/^\\.+/, \"\")).join(\"/\").replace(/\\.+$/, \"\")`.","Fix the generator deriving branch names from file paths so dotfiles are skipped or renamed.","Retype the branch name — trailing periods usually come from copy-pasting prose."],"exampleFix":"// before\nconst branch = filePath; // \".github/workflows\"\nvalidateBranchName(branch);\n// after\nconst branch = filePath.split(\"/\").map(p => p.replace(/^\\./, \"\")).join(\"/\"); // \"github/workflows\"\nvalidateBranchName(branch);","handlingStrategy":"validation","validationCode":"function hasDotIssues(name) {\n  return typeof name === \"string\" &&\n    (name.endsWith(\".\") || name.split(\"/\").some(p => p.startsWith(\".\")));\n}\nbranch = branch.split(\"/\").map(p => p.replace(/^\\.+/, \"\")).join(\"/\").replace(/\\.+$/, \"\");","typeGuard":"function isDotSafeBranch(v: unknown): v is string {\n  return typeof v === \"string\" && !v.endsWith(\".\") &&\n    !v.split(\"/\").some(p => p.startsWith(\".\"));\n}","tryCatchPattern":"try {\n  validateBranchName(branch, \"generator\");\n} catch (e) {\n  if (e instanceof InvalidBranchError && e.message.includes('\".\"')) {\n    branch = branch.split(\"/\").map(p => p.replace(/^\\./, \"\")).join(\"/\").replace(/\\.$/, \"\");\n    validateBranchName(branch, \"generator\");\n  } else throw e;\n}","preventionTips":["Do not derive branch names directly from filesystem paths; skip or rename dotfile segments.","Trim trailing punctuation when a branch name comes from copy-pasted text or prose.","Reject trailing '.' in any user-supplied ref input at your boundary.","Run generated names through git check-ref-format --branch in tests."],"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"}