JuliusBrussee/caveman · error
${label}:${lineNo}: duplicate key "${entry.key}"
Error message
${label}:${lineNo}: duplicate key "${entry.key}" What it means
Thrown by the catalog parser when the same key appears twice at indent 2 within one provider row. Because the parser builds a plain object per row, a duplicate key would silently overwrite the earlier value; rejecting it preserves the source's integrity and makes the generated catalog deterministic.
Source
Thrown at scripts/generate-agent-catalog.mjs:75
const indent = shape[1].length;
const dash = shape[2] !== undefined;
const rest = shape[3];
if (indent === 0) {
if (!dash) throw new Error(`${label}:${lineNo}: expected a top-level "- provider: ..." row`);
row = {};
rows.push(row);
blockKey = null;
const entry = splitKeyValue(rest, label, lineNo);
if (entry.value === undefined) throw new Error(`${label}:${lineNo}: row must start with an inline "provider" value`);
row[entry.key] = entry.value;
continue;
}
if (row === null) throw new Error(`${label}:${lineNo}: indented line before any row`);
if (indent === 2) {
if (dash) throw new Error(`${label}:${lineNo}: unexpected sequence item at row level`);
const entry = splitKeyValue(rest, label, lineNo);
if (Object.prototype.hasOwnProperty.call(row, entry.key)) {
throw new Error(`${label}:${lineNo}: duplicate key "${entry.key}"`);
}
if (entry.value === undefined) {
blockKey = entry.key;
row[entry.key] = undefined;
} else {
blockKey = null;
row[entry.key] = entry.value;
}
continue;
}
if (indent === 4) {
if (blockKey === null) throw new Error(`${label}:${lineNo}: nested line without an open block`);
if (dash) {
if (row[blockKey] === undefined) row[blockKey] = [];
if (!Array.isArray(row[blockKey])) throw new Error(`${label}:${lineNo}: "${blockKey}" mixes map and sequence entries`);
row[blockKey].push(rest);
continue;
}View on GitHub (pinned to 27d5a3981a)
Solutions
- Search the current row (from its "- provider:" line to the next one) for the duplicate key named in the error and delete or rename one occurrence.
- If you meant to have multiple values, use the two-level block form: " key:" with " - value" items.
Example fix
# before
- provider: anthropic
model: claude-4
model: claude-haiku
# after
- provider: anthropic
models:
- claude-4
- claude-haiku Defensive patterns
Strategy: validation
Validate before calling
function noDuplicateRowKeys(text) {
const seen = new Set(); let row = 0;
for (const line of text.split("\n")) {
if (/^- provider: /.test(line)) seen.clear();
const m = /^ {2}([A-Za-z_][A-Za-z0-9_]*):/.exec(line);
if (m) { if (seen.has(m[1])) return false; seen.add(m[1]); }
}
return true;
} Try / catch
try {
parseCatalog(text);
} catch (err) {
if (/duplicate key "/.test(err.message) && !err.message.includes(".")) {
// remove or rename the duplicated indent-2 key in the current row
} else throw err;
} Prevention
- Sort or lint keys within a row so duplicates are visually obvious.
- After merge conflicts, scan each row for repeated keys before running the generator.
When it happens
Trigger: Adding a property to a row that already defines it, e.g. two "model:" lines under one provider; pasting a block that re-declares keys already present higher in the row.
Common situations: Appending new config without scrolling to check the row's existing keys; merge conflicts resolved by keeping both sides' lines.
Related errors
- ${label}:${lineNo}: duplicate key "${blockKey}.${entry.key}"
- ${label}:${lineNo}: expected a top-level "- provider: ..." r
- ${label}:${lineNo}: row must start with an inline "provider"
- ${label}:${lineNo}: indented line before any row
- ${label}:${lineNo}: unexpected sequence item at row level
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/95f2694dac68f724.
Report an issue: GitHub.