tinyhumansai/openhuman · error · SpecError
duplicate id "${agent.id}"
Error message
duplicate id "${agent.id}" What it means
Agent ids must be unique across the batch; a second agent reusing an id already seen in the seenId Set throws with the duplicated value and path "agents[i].id". Ids key branch names and per-agent bookkeeping, so duplicates would alias work units. The error points at the second (offending) occurrence, not the original.
Source
Thrown at scripts/agent-batch/lib.mjs:104
const seenBranch = new Set();
for (let i = 0; i < spec.agents.length; i++) {
const agent = spec.agents[i];
const at = `agents[${i}]`;
if (!agent || typeof agent !== "object" || Array.isArray(agent)) {
throw new SpecError("must be an object", at);
}
for (const key of REQUIRED_AGENT) {
if (!(key in agent))
throw new SpecError(`missing required field "${key}"`, at);
}
if (typeof agent.id !== "string" || !/^a\d{2,3}$/.test(agent.id)) {
throw new SpecError(
`id must match /^a\\d{2,3}$/ (got "${agent.id}")`,
`${at}.id`,
);
}
if (seenId.has(agent.id))
throw new SpecError(`duplicate id "${agent.id}"`, `${at}.id`);
seenId.add(agent.id);
if (!Number.isInteger(agent.issue) || agent.issue <= 0) {
throw new SpecError("issue must be a positive integer", `${at}.issue`);
}
if (seenIssue.has(agent.issue)) {
throw new SpecError(`duplicate issue #${agent.issue}`, `${at}.issue`);
}
seenIssue.add(agent.issue);
if (typeof agent.title !== "string" || agent.title.trim().length === 0) {
throw new SpecError("title must be a non-empty string", `${at}.title`);
}
const m = BRANCH_RE.exec(agent.branch);
if (!m) {
throw new SpecError(
`branch must match cursor/<id>-<issue>-<slug> (got "${agent.branch}")`,
`${at}.branch`,
);
}View on GitHub (pinned to a221052e0d)
Solutions
- Renumber the agent reported in the error path to the next unused id (e.g. a03 → a04)
- Update that agent's branch id segment to match the new id
- Run a quick uniqueness pass: new Set(agents.map(a => a.id)).size === agents.length
Example fix
// before
{ "id": "a03", ... }
{ "id": "a03", ... } // duplicate
// after
{ "id": "a03", "branch": "cursor/a03-5314-...", ... }
{ "id": "a04", "branch": "cursor/a04-5315-...", ... } Defensive patterns
Strategy: validation
Validate before calling
const seen = new Set();
for (const [i, a] of spec.agents.entries()) {
if (seen.has(a.id)) console.error(`duplicate id ${a.id} at agents[${i}]`);
seen.add(a.id);
} Try / catch
try {
validateSpec(spec);
} catch (e) {
if (e instanceof SpecError && /duplicate id/.test(e.message)) {
console.error(`renumber the second occurrence: ${e.message}`);
process.exit(1);
}
throw e;
} Prevention
- After copy-pasting an agent, immediately bump its id, issue, and branch together
- Assert uniqueness before validating: new Set(agents.map(a => a.id)).size === agents.length
When it happens
Trigger: Copy-pasting an agent entry and editing its issue/title but forgetting to bump "id": two entries both with "a03"; a generator looping over a list with duplicate indices.
Common situations: Duplicating an agent as a starting point for a similar task; reordering entries and renumbering only half of them; merge conflicts in the spec JSON resolved by keeping both sides.
Related errors
- duplicate issue #${agent.issue}
- duplicate branch "${agent.branch}"
- batch_id must be a kebab-case slug
- base_repo must be "tinyhumansai/openhuman" (got "${spec.base
- base_branch must be "main" (got "${spec.base_branch}")
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/058a89342589ca50.
Report an issue: GitHub.