JuliusBrussee/caveman · error
${label}: ${key} has no pricing block
Error message
${label}: ${key} has no pricing block What it means
Thrown by selectRows() in scripts/generate-agent-catalog.mjs when a row is region-agnostic (`region: global`) and priced in USD but its `pricing` field is missing, null, an array, or a non-object. Regional rows and non-USD rows are skipped politely with a recorded reason; a global USD row is expected to be carried into the agent catalog, so lacking a pricing block entirely is treated as malformed data.
Source
Thrown at scripts/generate-agent-catalog.mjs:167
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");
continue;
}
if (seen.has(key)) throw new Error(`${label}: duplicate region-agnostic row ${key}`);
seen.add(key);
// reasoning_output_per_million is null on every provider that bills thinking
// tokens at the plain output rate. catalogCost subtracts reasoning tokens
// out of output before pricing them, so a 0 here would make them free.
const reasoning = rate(row.pricing.reasoning_output_per_million, `${key}.reasoning_output_per_million`, label);
selected.push({
key,
price: {
inputPerMillion: input,
outputPerMillion: output,View on GitHub (pinned to 27d5a3981a)
Solutions
- Add a `pricing:` block to the row with at least `input_per_million` and `output_per_million` (cache/reasoning fields optional, null allowed).
- If the row genuinely has no USD global list price, change `region` to a concrete region or `currency` to the real currency so the row is skipped with a reason instead of failing the build.
- Re-run the generator to confirm the row is selected or recorded as skipped.
Example fix
# before
- provider: acme
model: acme-1
region: global
currency: USD
# after
- provider: acme
model: acme-1
region: global
currency: USD
pricing:
input_per_million: 1.5
output_per_million: 6 Defensive patterns
Strategy: type-guard
Type guard
function hasPricingBlock(row) {
return row.pricing !== null && typeof row.pricing === "object" && !Array.isArray(row.pricing);
} Prevention
- Any row marked `region: global` + `currency: USD` must carry a pricing block — decide skip vs select consciously.
- If a model has no global USD price, set a concrete region or its true currency so it is skipped with a reason.
When it happens
Trigger: A row with `region: global` and `currency: USD` but no `pricing:` sub-block (or `pricing: null`, or `pricing:` populated with 4-space dash sequence items making it an array).
Common situations: Adding a new global model row and forgetting the pricing block; refactoring the catalog and dropping the nested block; a row that previously was regional flipped to global without adding prices.
Related errors
- %s: provider, model, and region are required
- %s %s/%s: unsupported currency %q
- %s %s/%s: invalid pricing
- ${label}:${lineNo}: "${text}" is not a finite number
- ${label}: row is missing a string "${field}"
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/07c688ced9957ff3.
Report an issue: GitHub.