JuliusBrussee/caveman · error · CatalogError
: pricing must be an object
Error message
{label}: pricing must be an object What it means
A row's `pricing` value is not a mapping (dict). Pricing must be an object whose keys are the known per-million rate fields; a scalar, list, or null cannot carry the rate data the cost engine reads. Thrown from validate_row.
Solutions
- Make pricing a mapping with at least input_per_million and output_per_million keys
- Restore indentation so rate keys sit under `pricing:` (two spaces deeper)
- For rates that don't apply, use explicit `null` values inside the object instead of omitting or flattening
- Copy the pricing block shape from a neighboring valid row
Example fix
# before pricing: 2.5 # after pricing: input_per_million: 2.5 output_per_million: 10.0 cache_read_input_per_million: null
Defensive patterns
Strategy: type-guard
Validate before calling
def pricing_is_object(row: dict) -> bool:
return isinstance(row.get("pricing"), dict) Type guard
def is_pricing_object(value: object) -> bool:
return isinstance(value, dict) and "input_per_million" in value Try / catch
try:
validate_catalog()
except CatalogError as e:
if "pricing must be an object" in str(e):
restructure_pricing_block(str(e)) Prevention
- Check indentation of the pricing block after hand edits
- Never flatten pricing to a single number; use null for non-applicable rates
- Diff YAML round-trip (load then dump) to catch structural drift
When it happens
Trigger: A row sets `pricing: 2.5` (a single rate instead of an object), `pricing: [2.5, 10]`, `pricing:` with nothing after it (YAML null), or indentation errors that collapse the pricing block into a string.
Common situations: Wrong indentation so the pricing sub-keys detach from `pricing:`; writing a flat number because only one rate was known; merge conflicts corrupting the block; a script emitting JSON arrays instead of objects.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- : unknown pricing field(s)
- : verified_at must be an RFC3339 timestamp
- catalog is empty
- catalog
- cave_budget_denomination_unavailable
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/49179d279d111d2f.
Report an issue: GitHub.
Appendix: source
Thrown at shared/provider-catalog/validate_catalog.py:135
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:
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")
View on GitHub (pinned to 3ee70a1026)