{"record":{"id":"7e912facf0b5c34d","repo":"abhigyanpatwari/GitNexus","slug":"source-branch-name-must-not-contain-consecutive-slashes","errorCode":null,"errorMessage":"${source}: branch name must not contain consecutive slashes.","messagePattern":"(.+?): branch name must not contain consecutive slashes\\.","errorType":"validation","errorClass":"InvalidBranchError","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/git-ref.ts","lineNumber":103,"sourceCode":"  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\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).","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/ac9a4e9abd8fd3058c070b72c23402a4f887929a/gitnexus/src/core/git-ref.ts#L85-L121","documentation":"validateBranchName rejects names containing '//' (consecutive slashes), matching git's ref-format rules; git itself never creates refs with empty path components. The library throws a descriptive InvalidBranchError early rather than letting the git subprocess fail later with a less clear message.","triggerScenarios":"Calling validateBranchName with 'feature//next' or names built by joining segments where an empty segment occurs, e.g. `${a}/${b}` with empty a or b, or double slashes from copy-pasted URLs/paths.","commonSituations":"CI environment variables that are empty or unset producing 'origin//branch'; naive path joins; pasting 'refs/heads//name' style strings.","solutions":["Collapse consecutive slashes: `name.replace(/\\/{2,}/g, \"/\")` before validating.","Fix the join logic so empty segments are filtered out (e.g. `[a, b].filter(Boolean).join(\"/\")`).","Verify the source variable (env var, config key) is actually populated."],"exampleFix":"// before\nconst branch = `${group}/${sub}/${name}`; // group empty -> \"//feature\"\nvalidateBranchName(branch);\n// after\nconst branch = [group, sub, name].filter(Boolean).join(\"/\");\nvalidateBranchName(branch);","handlingStrategy":"validation","validationCode":"function hasDoubleSlash(name) { return typeof name === \"string\" && name.includes(\"//\"); }\nbranch = String(branch ?? \"\").replace(/\\/{2,}/g, \"/\");","typeGuard":"function isSingleSlashed(v: unknown): v is string {\n  return typeof v === \"string\" && !v.includes(\"//\");\n}","tryCatchPattern":"try {\n  validateBranchName(branch, \"http\");\n} catch (e) {\n  if (e instanceof InvalidBranchError && e.message.includes(\"consecutive slashes\")) {\n    return res.status(400).json({ error: \"branch names must not contain '//'\" });\n  }\n  throw e;\n}","preventionTips":["Check that env vars interpolated into branch names are non-empty.","Normalize URLs/paths before reusing their segments as branch names.","Use defensive joins ([...parts].filter(Boolean).join('/')).","Add a unit test for names built from empty segments."],"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"}