{"record":{"id":"13ad63d5b2f9bdba","repo":"JuliusBrussee/caveman","slug":"label-lineno-cannot-read-text-as-a-ke","errorCode":null,"errorMessage":"${label}:${lineNo}: cannot read \"${text}\" as a \"key: value\" pair","messagePattern":"(.+?):(.+?): cannot read \"(.+?)\" as a \"key: value\" pair","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/generate-agent-catalog.mjs","lineNumber":113,"sourceCode":"      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;\n  if (text === \"true\") return true;\n  if (text === \"false\") return false;\n  if (/^-?(0|[1-9][0-9]*)(\\.[0-9]+)?([eE][+-]?[0-9]+)?$/.test(text)) {\n    const value = Number(text);\n    if (!Number.isFinite(value)) throw new Error(`${label}:${lineNo}: \"${text}\" is not a finite number`);\n    return value;\n  }\n  return text;\n}\n\n/** Selects the priced, region-agnostic, USD rows the agent catalog can honestly carry. */\nexport function selectRows(rows, label = CATALOG_LABEL) {","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/scripts/generate-agent-catalog.mjs#L95-L131","documentation":"Thrown by the purpose-built flat-YAML parser in scripts/generate-agent-catalog.mjs (splitKeyValue) when a catalog line does not match the strict `key: value` grammar `/^([A-Za-z_][A-Za-z0-9_]*):(?: (.*))?$/`. The generator intentionally avoids a full YAML library and only accepts identifier-style keys with at most one space after the colon; anything it does not recognize is a hard error, it never guesses. The message includes the catalog label and 1-based line number so the offending line can be fixed directly.","triggerScenarios":"A line inside a row block like `input-per-million: 0.25` (hyphen in key), `my.key: 1` (dot in key), `foo :bar` or `foo:bar` (wrong spacing around the colon), a key starting with a digit, or a stray prose line where a `key: value` entry was expected. Any of these makes the regex exec return null and this error fires.","commonSituations":"Hand-editing public/shared/provider-catalog/catalog/current.yaml after copying keys from provider pricing docs (which use hyphens or dots); pasting rows from a richer YAML file that uses anchors, quotes, or flow syntax the mini-parser does not support; CI failing on catalog.drift.runtime.mjs after an edit and re-running the generator.","solutions":["Open the file and line named in the message and rewrite the line as `key: value` where key matches [A-Za-z_][A-Za-z0-9_]* (use snake_case) and there is exactly one space after the colon.","Remove YAML features the mini-parser does not support (quoted keys, anchors, flow maps like {a: 1}); keep the flat two-level `- key:` row shape documented at the top of the script.","Re-run `node scripts/generate-agent-catalog.mjs` to confirm the catalog now parses and regenerates public/agent/src/catalog.ts."],"exampleFix":"# before (catalog current.yaml)\n  input-per-million: 0.25\n# after\n  input_per_million: 0.25","handlingStrategy":"validation","validationCode":"// Pre-validate catalog lines against the generator's grammar before running it.\nimport { readFileSync } from \"node:fs\";\nconst KEY_VALUE = /^([A-Za-z_][A-Za-z0-9_]*):(?: (.*))?$/;\nfunction checkCatalog(text) {\n  const bad = [];\n  text.split(\"\\n\").forEach((line, i) => {\n    const l = line.replace(/\\s+$/, \"\");\n    if (l === \"\" || /^ *#/.test(l)) return;\n    const rest = /^( *)(- )?(.*)$/.exec(l)[3];\n    if (rest.includes(\":\") && !KEY_VALUE.test(rest) && !rest.startsWith(\"- \")) bad.push(i + 1);\n  });\n  return bad;\n}","typeGuard":null,"tryCatchPattern":"try {\n  execFileSync(\"node\", [\"scripts/generate-agent-catalog.mjs\"]);\n} catch (error) {\n  // messages are `${label}:${lineNo}: ...` — split on the first colon pair\n  const m = /^(.*):(\\d+): (.*)$/.exec(error.stderr?.toString() ?? error.message);\n  if (m) reportCatalogIssue(m[1], Number(m[2]), m[3]);\n  else throw error;\n}","preventionTips":["Keep catalog keys snake_case; provider docs' hyphenated names must be translated.","Run the generator locally right after editing current.yaml instead of waiting for the drift test in CI.","Enable an editor lint that flags trailing whitespace in YAML files."],"tags":["yaml","parsing","build-tooling","catalog"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}