JuliusBrussee/caveman · error
${label}: row is missing a string "${field}"
Error message
${label}: row is missing a string "${field}" What it means
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.
Source
Thrown at scripts/generate-agent-catalog.mjs:154
let entry = skippedByKey.get(key);
if (entry === undefined) {
entry = { regions: [], reasons: [] };
skippedByKey.set(key, entry);
}
return entry;
};
const skipRegional = (key, region) => {
const entry = record(key);
if (!entry.regions.includes(region)) entry.regions.push(region);
};
const skip = (key, reason) => {
const entry = record(key);
if (!entry.reasons.includes(reason)) entry.reasons.push(reason);
};
for (const row of rows) {
for (const field of ["provider", "model", "region", "currency"]) {
if (typeof row[field] !== "string" || row[field] === "") {
throw new Error(`${label}: row is missing a string "${field}"`);
}
}
const key = `${row.provider}/${row.model}`;
if (row.region !== REGION_AGNOSTIC) {
skipRegional(key, row.region);
continue;
}
if (row.currency !== REQUIRED_CURRENCY) {
skip(key, `priced in ${row.currency}, not ${REQUIRED_CURRENCY}`);
continue;
}
if (row.pricing === null || typeof row.pricing !== "object" || Array.isArray(row.pricing)) {
throw new Error(`${label}: ${key} has no pricing block`);
}
const input = rate(row.pricing.input_per_million, `${key}.input_per_million`, label);
const output = rate(row.pricing.output_per_million, `${key}.output_per_million`, label);
if (input === null || output === null) {
skip(key, "no list price for input or output tokens");View on GitHub (pinned to 27d5a3981a)
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.
Example fix
# before - provider: acme model: acme-1 region: global # after - provider: acme model: acme-1 region: global currency: USD
Defensive patterns
Strategy: type-guard
Type guard
function isCatalogRow(row) {
return row !== null && typeof row === "object"
&& ["provider", "model", "region", "currency"].every(
(f) => typeof row[f] === "string" && row[f] !== ""
);
} Prevention
- 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.
When it happens
Trigger: 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`.
Common situations: 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.
Related errors
- ${label}:${lineNo}: "${text}" is not a finite number
- ${label}: ${key} has no pricing block
- %s: provider, model, and region are required
- %s %s/%s: unsupported currency %q
- %s %s/%s: invalid pricing
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/2aa761802473dfe7.
Report an issue: GitHub.