JuliusBrussee/caveman · error · CatalogError

: missing field(s)

Error message

{label}: missing field(s): {', '.join(sorted(missing))}

What it means

A required top-level field is absent from a catalog row. Every row must carry provider, model, region, currency, pricing, capabilities, sources, and verified_at (capabilities_verified_at is optional); a missing key means the row cannot be identified, priced, or snapshot-pinned. Thrown from validate_row.

Solutions

  1. Add the listed missing field(s) to the row, copying structure from a neighboring valid row
  2. For pricing fields that don't apply, include them inside `pricing` as explicit null rather than omitting row-level keys
  3. If a sync script produced the row, fix its template to emit all required keys
  4. Run the validator again — it reports all missing fields for the row at once

Example fix

# before
- provider: openai
  model: gpt-4o
  region: global

# after
- provider: openai
  model: gpt-4o
  region: global
  currency: USD
  pricing: {input_per_million: 2.5, output_per_million: 10.0}
  capabilities: {}
  sources: ["https://openai.com/pricing"]
  verified_at: "2026-07-05T00:00:00Z"
Defensive patterns

Strategy: validation

Validate before calling

REQUIRED_KEYS = {"provider","model","region","currency","pricing","capabilities","sources","verified_at"}

def all_required_present(row: dict) -> bool:
    return not (REQUIRED_KEYS - set(row))

Type guard

def is_complete_row(row: object) -> bool:
    return isinstance(row, dict) and REQUIRED_KEYS.issubset(row)

Try / catch

try:
    validate_catalog()
except CatalogError as e:
    if "missing field(s)" in str(e):
        fill_missing_fields(str(e))

Prevention

When it happens

Trigger: validate_row computes `REQUIRED_KEYS - set(row)` non-empty, e.g. a newly added row forgetting `sources`, a snapshot row copied without `verified_at`, or a bulk edit deleting a key. The message lists exactly which fields are missing.

Common situations: Adding a new model row by hand and forgetting one of the eight fields; a sync script emitting partial rows; deleting a field during a merge-conflict resolution; copying a row template that predates a required field.

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/236ece813cc5a956. Report an issue: GitHub.

Appendix: source

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

    elif isinstance(value, str):
        try:
            parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
        except ValueError as error:
            raise CatalogError(f"{label}: verified_at must be RFC3339") from error
    else:
        raise CatalogError(f"{label}: verified_at must be an RFC3339 timestamp")
    if parsed.tzinfo is None:
        raise CatalogError(f"{label}: verified_at must include a timezone")
    return parsed.astimezone(timezone.utc)


def validate_row(row: dict[str, Any], label: str, now: datetime) -> tuple[str, str, str]:
    unknown = set(row) - TOP_LEVEL_KEYS
    missing = REQUIRED_KEYS - set(row)
    if unknown:
        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:

View on GitHub (pinned to 3ee70a1026)