JuliusBrussee/caveman · error · CatalogError

: unknown pricing field(s)

Error message

{label}: unknown pricing field(s): {', '.join(sorted(pricing_unknown))}

What it means

The `pricing` object contains a key outside the allowlisted PRICING_KEYS set (the twelve known per-million rate/threshold fields). Like the top-level schema, pricing is strict-allowlist so misspelled or invented rate names cannot silently produce zero-cost math. Thrown from validate_row.

Solutions

  1. Rename the listed key(s) to the exact allowed names (e.g. `input_per_million`, `output_per_million`, `cache_read_input_per_million`)
  2. Check PRICING_KEYS in validate_catalog.py for the exact spelling of each field
  3. If a genuinely new pricing field is needed, add it to PRICING_KEYS and schemas/provider-catalog.schema.json in the same PR
  4. Map third-party field names to the schema's snake_case names in any sync script

Example fix

# before
pricing:
  input_per_million_tokens: 2.5

# after
pricing:
  input_per_million: 2.5
Defensive patterns

Strategy: validation

Validate before calling

PRICING_KEYS = {"input_per_million","output_per_million","cache_read_input_per_million","cache_write_input_per_million","cache_write_1h_input_per_million","reasoning_output_per_million","batch_discount_fraction","cache_storage_per_million_tokens_hour","long_context_threshold_tokens","long_context_threshold_inclusive","long_context_input_multiplier","long_context_output_multiplier"}

def pricing_keys_ok(row: dict) -> bool:
    p = row.get("pricing")
    return isinstance(p, dict) and not (set(p) - PRICING_KEYS)

Type guard

def has_known_pricing_keys(pricing: dict) -> bool:
    return set(pricing).issubset(PRICING_KEYS)

Try / catch

try:
    validate_catalog()
except CatalogError as e:
    if "unknown pricing field(s)" in str(e):
        rename_pricing_keys(str(e))

Prevention

When it happens

Trigger: pricing has keys like `input_per_million_tokens`, `inputPerMillion`, `output`, `cache_read`, or a renamed/typo'd variant of an allowed key, making `set(pricing) - PRICING_KEYS` non-empty.

Common situations: Typo in an allowed key name; a script using camelCase or a third-party API's field names; inventing a new pricing field without registering it in PRICING_KEYS and schemas/provider-catalog.schema.json; copying field names from a provider's API docs instead of this schema.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/a4c522b56ebb7629. Report an issue: GitHub.

Appendix: source

Thrown at shared/provider-catalog/validate_catalog.py:138

        raise CatalogError(f"{label}: unknown field(s): {', '.join(sorted(unknown))}")
    if missing:
        raise CatalogError(f"{label}: missing field(s): {', '.join(sorted(missing))}")

    identity: list[str] = []
    for field in ("provider", "model", "region"):
        value = row[field]
        if not isinstance(value, str) or not IDENTIFIER.fullmatch(value):
            raise CatalogError(f"{label}: {field} must be a safe non-empty identifier")
        identity.append(value)
    if row["currency"] != "USD":
        raise CatalogError(f"{label}: currency must be USD")

    pricing = row["pricing"]
    if not isinstance(pricing, dict):
        raise CatalogError(f"{label}: pricing must be an object")
    pricing_unknown = set(pricing) - PRICING_KEYS
    if pricing_unknown:
        raise CatalogError(f"{label}: unknown pricing field(s): {', '.join(sorted(pricing_unknown))}")
    for required in ("input_per_million", "output_per_million"):
        if required not in pricing:
            raise CatalogError(f"{label}: pricing.{required} is required")
    for field, value in pricing.items():
        if value is None or field == "long_context_threshold_inclusive":
            if field == "long_context_threshold_inclusive" and value is not None and not isinstance(value, bool):
                raise CatalogError(f"{label}: pricing.{field} must be boolean or null")
            continue
        if isinstance(value, bool) or not isinstance(value, (int, float)):
            raise CatalogError(f"{label}: pricing.{field} must be numeric or null")
        if not math.isfinite(value) or value < 0:
            raise CatalogError(f"{label}: pricing.{field} must be finite and nonnegative")
        if field == "batch_discount_fraction" and value > 1:
            raise CatalogError(f"{label}: pricing.batch_discount_fraction must be <= 1")

    if not isinstance(row["capabilities"], dict):
        raise CatalogError(f"{label}: capabilities must be an object")
    sources = row["sources"]

View on GitHub (pinned to 3ee70a1026)