{"record":{"id":"2aa761802473dfe7","repo":"JuliusBrussee/caveman","slug":"label-row-is-missing-a-string-field","errorCode":null,"errorMessage":"${label}: row is missing a string \"${field}\"","messagePattern":"(.+?): row is missing a string \"(.+?)\"","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/generate-agent-catalog.mjs","lineNumber":154,"sourceCode":"    let entry = skippedByKey.get(key);\n    if (entry === undefined) {\n      entry = { regions: [], reasons: [] };\n      skippedByKey.set(key, entry);\n    }\n    return entry;\n  };\n  const skipRegional = (key, region) => {\n    const entry = record(key);\n    if (!entry.regions.includes(region)) entry.regions.push(region);\n  };\n  const skip = (key, reason) => {\n    const entry = record(key);\n    if (!entry.reasons.includes(reason)) entry.reasons.push(reason);\n  };\n  for (const row of rows) {\n    for (const field of [\"provider\", \"model\", \"region\", \"currency\"]) {\n      if (typeof row[field] !== \"string\" || row[field] === \"\") {\n        throw new Error(`${label}: row is missing a string \"${field}\"`);\n      }\n    }\n    const key = `${row.provider}/${row.model}`;\n    if (row.region !== REGION_AGNOSTIC) {\n      skipRegional(key, row.region);\n      continue;\n    }\n    if (row.currency !== REQUIRED_CURRENCY) {\n      skip(key, `priced in ${row.currency}, not ${REQUIRED_CURRENCY}`);\n      continue;\n    }\n    if (row.pricing === null || typeof row.pricing !== \"object\" || Array.isArray(row.pricing)) {\n      throw new Error(`${label}: ${key} has no pricing block`);\n    }\n    const input = rate(row.pricing.input_per_million, `${key}.input_per_million`, label);\n    const output = rate(row.pricing.output_per_million, `${key}.output_per_million`, label);\n    if (input === null || output === null) {\n      skip(key, \"no list price for input or output tokens\");","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/scripts/generate-agent-catalog.mjs#L136-L172","documentation":"Thrown by selectRows() in scripts/generate-agent-catalog.mjs when a parsed catalog row lacks one of the four mandatory string fields: provider, model, region, or currency (missing key, non-string value, or empty string). Every row must carry these before any skip/keep decision is made, so a row that cannot even be identified is a hard parse-integrity error rather than a skippable row.","triggerScenarios":"A `- provider: x` row block in current.yaml that omits `model:`, `region:`, or `currency:` at the 2-space level, or sets one of them to null (e.g. `model: null`), or to a non-string scalar such as `region: true` or `currency: 1`.","commonSituations":"Adding a new provider row and forgetting the region or currency field; setting `model: null` as a placeholder; renaming a field so the expected key no longer exists.","solutions":["Find the row named in the message and add the missing field with a non-empty string value (`region: global`, `currency: USD`, `model: <id>`).","If the field was set to null/true/a number, change it to a plain string scalar.","Re-run the generator; the row will then flow into the normal regional/currency skip logic."],"exampleFix":"# before\n- provider: acme\n  model: acme-1\n  region: global\n# after\n- provider: acme\n  model: acme-1\n  region: global\n  currency: USD","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isCatalogRow(row) {\n  return row !== null && typeof row === \"object\"\n    && [\"provider\", \"model\", \"region\", \"currency\"].every(\n      (f) => typeof row[f] === \"string\" && row[f] !== \"\"\n    );\n}","tryCatchPattern":null,"preventionTips":["When adding a provider row, copy an existing complete row and edit values only.","Keep a schema note at the top of the catalog listing the four mandatory string fields."],"tags":["yaml","validation","catalog","schema"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}