{"record":{"id":"9186ae22c21dd595","repo":"JuliusBrussee/caveman","slug":"label-lineno-nested-line-without-an-open-bl","errorCode":null,"errorMessage":"${label}:${lineNo}: nested line without an open block","messagePattern":"(.+?):(.+?): nested line without an open block","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/generate-agent-catalog.mjs","lineNumber":87,"sourceCode":"    }\n    if (row === null) throw new Error(`${label}:${lineNo}: indented line before any row`);\n    if (indent === 2) {\n      if (dash) throw new Error(`${label}:${lineNo}: unexpected sequence item at row level`);\n      const entry = splitKeyValue(rest, label, lineNo);\n      if (Object.prototype.hasOwnProperty.call(row, entry.key)) {\n        throw new Error(`${label}:${lineNo}: duplicate key \"${entry.key}\"`);\n      }\n      if (entry.value === undefined) {\n        blockKey = entry.key;\n        row[entry.key] = undefined;\n      } else {\n        blockKey = null;\n        row[entry.key] = entry.value;\n      }\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    }","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/scripts/generate-agent-catalog.mjs#L69-L105","documentation":"Thrown by the catalog parser when a line at indent 4 (the nested block level) appears while blockKey is null — i.e. no indent-2 key was left \"open\" as a valueless block header. Indent-4 content is only meaningful as the contents of a block key like \"  models:\"; without one, the parser cannot tell which property the nested line belongs to.","triggerScenarios":"Writing indent-4 lines directly after an inline-valued property, e.g. \"  models: default\" followed by \"    - claude-4\"; or deleting/commenting the \"  key:\" header line while leaving its children.","commonSituations":"Editor auto-indent carrying 4 spaces onto a line that should be at 2; converting an inline value to a list but forgetting to strip the inline value; merge conflict cleanup removing the block header.","solutions":["Insert the block header at indent 2 with no inline value above the nested lines: \"  models:\" then the indent-4 items.","If the indent-4 line is really a row property, dedent it to 2 spaces.","Remove inline values from keys that are meant to open blocks — \"key: value\" closes the block, \"key:\" opens it."],"exampleFix":"# before\n- provider: anthropic\n  models: all\n    - claude-4\n\n# after\n- provider: anthropic\n  models:\n    - claude-4","handlingStrategy":"validation","validationCode":"function indent4AlwaysUnderOpenBlock(text) {\n  let openBlock = false;\n  for (const line of text.split(\"\\n\")) {\n    if (/^ {2}[A-Za-z_][A-Za-z0-9_]*:\\s*$/.test(line.replace(/\\s+$/, \"\"))) openBlock = true;\n    else if (/^ {2}\\S/.test(line)) openBlock = false;\n    else if (/^ {4}/.test(line) && !openBlock) return false;\n  }\n  return true;\n}","typeGuard":null,"tryCatchPattern":"try {\n  parseCatalog(text);\n} catch (err) {\n  if (/nested line without an open block/.test(err.message)) {\n    // add the valueless indent-2 key header above the indent-4 lines\n  } else throw err;\n}","preventionTips":["Remember the rule: \"key: value\" closes a block, \"key:\" alone opens one.","When converting an inline value to a list, delete the inline value in the same edit."],"tags":["yaml","parser","indentation","code-generation"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}