{"record":{"id":"bb65fb52128c6176","repo":"JuliusBrussee/caveman","slug":"label-lineno-duplicate-key-blockkey-e","errorCode":null,"errorMessage":"${label}:${lineNo}: duplicate key \"${blockKey}.${entry.key}\"","messagePattern":"(.+?):(.+?): duplicate key \"(.+?)\\.(.+?)\"","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/generate-agent-catalog.mjs","lineNumber":101,"sourceCode":"      }\n      continue;\n    }\n    if (indent === 4) {\n      if (blockKey === null) throw new Error(`${label}:${lineNo}: nested line without an open block`);\n      if (dash) {\n        if (row[blockKey] === undefined) row[blockKey] = [];\n        if (!Array.isArray(row[blockKey])) throw new Error(`${label}:${lineNo}: \"${blockKey}\" mixes map and sequence entries`);\n        row[blockKey].push(rest);\n        continue;\n      }\n      if (row[blockKey] === undefined) row[blockKey] = {};\n      if (Array.isArray(row[blockKey]) || typeof row[blockKey] !== \"object\") {\n        throw new Error(`${label}:${lineNo}: \"${blockKey}\" mixes map and sequence entries`);\n      }\n      const entry = splitKeyValue(rest, label, lineNo);\n      if (entry.value === undefined) throw new Error(`${label}:${lineNo}: nesting deeper than two levels is not supported`);\n      if (Object.prototype.hasOwnProperty.call(row[blockKey], entry.key)) {\n        throw new Error(`${label}:${lineNo}: duplicate key \"${blockKey}.${entry.key}\"`);\n      }\n      row[blockKey][entry.key] = entry.value;\n      continue;\n    }\n    throw new Error(`${label}:${lineNo}: unexpected indent of ${indent} spaces`);\n  }\n  return rows;\n}\n\nfunction splitKeyValue(text, label, lineNo) {\n  const match = /^([A-Za-z_][A-Za-z0-9_]*):(?: (.*))?$/.exec(text);\n  if (match === null) throw new Error(`${label}:${lineNo}: cannot read \"${text}\" as a \"key: value\" pair`);\n  return { key: match[1], value: match[2] === undefined ? undefined : scalar(match[2], label, lineNo) };\n}\n\nfunction scalar(text, label, lineNo) {\n  if (text === \"\") throw new Error(`${label}:${lineNo}: empty value`);\n  if (text === \"null\") return null;","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/scripts/generate-agent-catalog.mjs#L83-L119","documentation":"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.","triggerScenarios":"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.","commonSituations":"Merging edits where both branches added the same sub-key; duplicating a neighboring line as a template and forgetting to rename the key.","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."],"exampleFix":"# before\n- provider: anthropic\n  endpoints:\n    default: /v1/messages\n    default: /v1/count_tokens\n\n# after\n- provider: anthropic\n  endpoints:\n    messages: /v1/messages\n    count_tokens: /v1/count_tokens","handlingStrategy":"validation","validationCode":"function noDuplicateBlockKeys(text) {\n  const blocks = new Map();\n  for (const line of text.split(\"\\n\")) {\n    const header = /^ {2}([A-Za-z_][A-Za-z0-9_]*):\\s*$/.exec(line.replace(/\\s+$/, \"\"));\n    if (header) { blocks.set(header[1], new Set()); continue; }\n    const entry = /^ {4}([A-Za-z_][A-Za-z0-9_]*):/.exec(line);\n    if (entry) {\n      for (const [name, set] of blocks) { if (set.has(entry[1])) return false; set.add(entry[1]); break; }\n    }\n  }\n  return true;\n}","typeGuard":null,"tryCatchPattern":"try {\n  parseCatalog(text);\n} catch (err) {\n  if (/duplicate key \".*\\./.test(err.message)) {\n    // the message gives blockKey.entryKey — dedupe within that block\n  } else throw err;\n}","preventionTips":["Rename duplicated sub-keys to descriptive unique names instead of repeating.","Lint the catalog file for duplicate keys in CI."],"tags":["yaml","parser","duplicate-key","code-generation"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}