JuliusBrussee/caveman · error
${label}:${lineNo}: duplicate key "${blockKey}.${entry.key}"
Error message
${label}:${lineNo}: duplicate key "${blockKey}.${entry.key}" What it means
Thrown by the catalog parser when the same sub-key appears twice inside one indent-4 map block. Like the row-level duplicate-key check, it exists because the parser writes into a plain object; a duplicate would silently clobber the earlier value and make generated output depend on line order.
Source
Thrown at scripts/generate-agent-catalog.mjs:101
}
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;
}
if (row[blockKey] === undefined) row[blockKey] = {};
if (Array.isArray(row[blockKey]) || typeof row[blockKey] !== "object") {
throw new Error(`${label}:${lineNo}: "${blockKey}" mixes map and sequence entries`);
}
const entry = splitKeyValue(rest, label, lineNo);
if (entry.value === undefined) throw new Error(`${label}:${lineNo}: nesting deeper than two levels is not supported`);
if (Object.prototype.hasOwnProperty.call(row[blockKey], entry.key)) {
throw new Error(`${label}:${lineNo}: duplicate key "${blockKey}.${entry.key}"`);
}
row[blockKey][entry.key] = entry.value;
continue;
}
throw new Error(`${label}:${lineNo}: unexpected indent of ${indent} spaces`);
}
return rows;
}
function splitKeyValue(text, label, lineNo) {
const match = /^([A-Za-z_][A-Za-z0-9_]*):(?: (.*))?$/.exec(text);
if (match === null) throw new Error(`${label}:${lineNo}: cannot read "${text}" as a "key: value" pair`);
return { key: match[1], value: match[2] === undefined ? undefined : scalar(match[2], label, lineNo) };
}
function scalar(text, label, lineNo) {
if (text === "") throw new Error(`${label}:${lineNo}: empty value`);
if (text === "null") return null;View on GitHub (pinned to 27d5a3981a)
Solutions
- In the block named by blockKey in the message, find the two occurrences of entry.key and delete or rename one.
- If both values are needed, rename to distinct keys (e.g. default_v1/default_v2) or restructure as a list where duplicates are legitimate distinct items.
Example fix
# before
- provider: anthropic
endpoints:
default: /v1/messages
default: /v1/count_tokens
# after
- provider: anthropic
endpoints:
messages: /v1/messages
count_tokens: /v1/count_tokens Defensive patterns
Strategy: validation
Validate before calling
function noDuplicateBlockKeys(text) {
const blocks = new Map();
for (const line of text.split("\n")) {
const header = /^ {2}([A-Za-z_][A-Za-z0-9_]*):\s*$/.exec(line.replace(/\s+$/, ""));
if (header) { blocks.set(header[1], new Set()); continue; }
const entry = /^ {4}([A-Za-z_][A-Za-z0-9_]*):/.exec(line);
if (entry) {
for (const [name, set] of blocks) { if (set.has(entry[1])) return false; set.add(entry[1]); break; }
}
}
return true;
} Try / catch
try {
parseCatalog(text);
} catch (err) {
if (/duplicate key ".*\./.test(err.message)) {
// the message gives blockKey.entryKey — dedupe within that block
} else throw err;
} Prevention
- Rename duplicated sub-keys to descriptive unique names instead of repeating.
- Lint the catalog file for duplicate keys in CI.
When it happens
Trigger: Within one block (e.g. " endpoints:"), writing " default: /a" twice, or pasting a block that re-declares a sub-key already present earlier in the same block.
Common situations: Merging edits where both branches added the same sub-key; duplicating a neighboring line as a template and forgetting to rename the key.
Related errors
- ${label}:${lineNo}: duplicate key "${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/bb65fb52128c6176.
Report an issue: GitHub.