tinyhumansai/openhuman · error · SpecError
duplicate branch "${agent.branch}"
Error message
duplicate branch "${agent.branch}" What it means
Branch strings must be unique across the batch — the seenBranch Set rejects a second agent declaring the same branch, with the duplicated value and path "agents[i].branch". Uniqueness matters because each agent's work is pushed to its own git branch; two agents sharing one would clobber each other. Given the id/issue cross-checks, this usually means fully duplicated entries.
Source
Thrown at scripts/agent-batch/lib.mjs:136
throw new SpecError(
`branch must match cursor/<id>-<issue>-<slug> (got "${agent.branch}")`,
`${at}.branch`,
);
}
if (m[1] !== agent.id) {
throw new SpecError(
`branch id segment "${m[1]}" does not match agent id "${agent.id}"`,
`${at}.branch`,
);
}
if (Number(m[2]) !== agent.issue) {
throw new SpecError(
`branch issue segment "${m[2]}" does not match agent issue ${agent.issue}`,
`${at}.branch`,
);
}
if (seenBranch.has(agent.branch)) {
throw new SpecError(`duplicate branch "${agent.branch}"`, `${at}.branch`);
}
seenBranch.add(agent.branch);
if (!Array.isArray(agent.owned_paths) || agent.owned_paths.length === 0) {
throw new SpecError(
"owned_paths must be a non-empty array",
`${at}.owned_paths`,
);
}
for (let j = 0; j < agent.owned_paths.length; j++) {
const p = agent.owned_paths[j];
if (typeof p !== "string" || p.length === 0) {
throw new SpecError(
"must be a non-empty string",
`${at}.owned_paths[${j}]`,
);
}
if (p.includes("*") || p.includes("?")) {
throw new SpecError(View on GitHub (pinned to a221052e0d)
Solutions
- Make each branch unique by construction: cursor/<id>-<issue>-<slug> with that agent's own id and issue
- Since id and issue are already forced unique, a duplicate branch almost always means the branch was not regenerated after fixing those fields — regenerate it
- Check: new Set(agents.map(a => a.branch)).size === agents.length
Example fix
// before
{ "id": "a01", "issue": 5312, "branch": "cursor/a01-5312-shared-slug" }
{ "id": "a02", "issue": 5313, "branch": "cursor/a01-5312-shared-slug" }
// after
{ "id": "a01", "issue": 5312, "branch": "cursor/a01-5312-fix-ci-timeout" }
{ "id": "a02", "issue": 5313, "branch": "cursor/a02-5313-tighten-lint" } Defensive patterns
Strategy: validation
Validate before calling
const branches = spec.agents.map((a) => a.branch);
if (new Set(branches).size !== branches.length) {
console.error("duplicate branch names across agents");
process.exit(1);
} Try / catch
try {
validateSpec(spec);
} catch (e) {
if (e instanceof SpecError && /duplicate branch/.test(e.message)) {
console.error(`regenerate the copied branch: ${e.message}`);
process.exit(1);
}
throw e;
} Prevention
- Because id and issue are already forced unique, always regenerate the whole branch string after fixing either field
- Assert branch uniqueness alongside id/issue uniqueness in your pre-flight check
When it happens
Trigger: Two agents with identical branch strings — typically a whole duplicated agent entry where id/issue checks were fixed to be distinct but the branch slug was left identical, or the same agent pasted twice with only cosmetic edits.
Common situations: Copy-paste growth of a spec where branches were templated as cursor/a01-5312-... and never varied; merge of two spec files that both contain an agent with the same branch.
Related errors
- base_branch must be "main" (got "${spec.base_branch}")
- duplicate id "${agent.id}"
- duplicate issue #${agent.issue}
- branch must match cursor/<id>-<issue>-<slug> (got "${agent.b
- batch_id must be a kebab-case slug
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/17c15b7e4ddfe3e2.
Report an issue: GitHub.