JuliusBrussee/caveman · error · CatalogError
{label}: pricing changed without a new verified_at snapshot
Error message
{label}: pricing changed without a new verified_at snapshot version (checked catalog/{verified}.yaml) What it means
Raised by validate_catalog() when a current.yaml row's pricing identity (provider, model, region, currency, pricing, verified_at, plus price-affecting capabilities) has no byte-semantic match in the immutable dated snapshot catalog/<verified_at>.yaml. The snapshot is the tamper-evidence for the price an earlier verified_at already attested — if pricing changed while verified_at stayed put, the change ships under an attestation nobody made, so validation refuses it.
Source
Thrown at shared/provider-catalog/validate_catalog.py:234
for index, row in enumerate(current):
label = f"current.yaml row {index + 1}"
identity = validate_row(row, label, check_time)
if identity in seen:
raise CatalogError(f"{label}: duplicate identity {'/'.join(identity)}")
seen.add(identity)
verified = parsed_time(row["verified_at"], label).date().isoformat()
snapshot = snapshots.setdefault(
verified,
load_yaml(CATALOG_DIR / f"{verified}.yaml"),
)
identity = pricing_identity(row)
matched = next(
(snap_row for snap_row in snapshot if pricing_identity(snap_row) == identity),
None,
)
if matched is None:
raise CatalogError(
f"{label}: pricing changed without a new verified_at snapshot version (checked catalog/{verified}.yaml)"
)
# sources is excluded from pricing_identity so a capability citation can
# be added without minting a new price-dated snapshot, but a PRICING
# citation must never be silently swapped or dropped while verified_at
# stays put. Mirrors the Go isSubset check in catalog_test.go.
snapshot_sources = set(matched.get("sources") or [])
if not snapshot_sources.issubset(set(row["sources"])):
raise CatalogError(
f"{label}: dropped or replaced a source from its immutable snapshot "
f"catalog/{verified}.yaml without a new verified_at "
f"(snapshot had {sorted(snapshot_sources)}, current has {sorted(row['sources'])})"
)
def main() -> int:
try:
validate_catalog()View on GitHub (pinned to 27d5a3981a)
Solutions
- If the price genuinely changed: bump verified_at to the day you re-checked it, and create (or extend) catalog/<that-date>.yaml containing the row with its new pricing — the snapshot must contain a row whose pricing identity matches exactly.
- If you bumped verified_at already, verify catalog/<verified_at>.yaml exists and contains the row (load_yaml is called on it lazily; a missing file errors as invalid YAML/missing file).
- If the edit was accidental (typo), revert the pricing fields to match the snapshot instead of minting a new one.
- Remember capabilities edits that are NOT price-affecting (tools/vision/json_mode/context_window_tokens) do not require a snapshot — only pricing and the three price-affecting capability keys do.
Example fix
# before — price edited in place, verified_at unchanged (no snapshot match)
- provider: openai
model: gpt-4o
region: us
pricing: {input_per_million: 2.75} # was 2.50 in catalog/2026-06-01.yaml
verified_at: "2026-06-01T00:00:00Z"
# after — re-verified, new date + new snapshot catalog/2026-08-15.yaml with the same row
- provider: openai
model: gpt-4o
region: us
pricing: {input_per_million: 2.75}
verified_at: "2026-08-15T00:00:00Z" Defensive patterns
Strategy: validation
Validate before calling
PRICING_IDENTITY_KEYS = ("provider", "model", "region", "currency", "pricing", "verified_at")
PRICE_AFFECTING_CAPS = ("regional_processing_multiplier", "inference_geo_us_multiplier", "region_agnostic_pricing")
def snapshot_pins_row(current_row: dict, snapshot_rows: list[dict]) -> bool:
def ident(r):
caps = {k: r.get("capabilities", {}).get(k) for k in PRICE_AFFECTING_CAPS}
return tuple(r.get(k) for k in PRICING_IDENTITY_KEYS), tuple(sorted(caps.items()))
return any(ident(s) == ident(current_row) for s in snapshot_rows) Try / catch
try:
validate_catalog()
except CatalogError as e:
if "without a new verified_at snapshot" in str(e):
# bump verified_at to today and copy the row into catalog/<today>.yaml
mint_snapshot_for_changed_rows()
raise Prevention
- Pair every pricing (or price-affecting capability) edit with a verified_at bump and a dated snapshot in the same commit.
- Remember the three price-affecting capability keys count as pricing even though they live under capabilities.
- Run go test ./public/shared/platform/catalog as the same gate — it enforces the identical pin.
When it happens
Trigger: Editing any pricing field (or a PriceAffectingCapabilities key like regional_processing_multiplier, inference_geo_us_multiplier, region_agnostic_pricing) without bumping verified_at; bumping verified_at but forgetting to create/update catalog/<new-date>.yaml; fixing a typo in an existing price in place; or reusing an old verified_at date for a changed price.
Common situations: A quick 'just fix the number' edit to current.yaml; a new model added to current.yaml but not copied into a dated snapshot; snapshot files pruned by an over-eager cleanup that deleted a referenced date.
Related errors
- {label}: verified_at is in the future
- {label}: verified_at is older than 120 days
- {label}: unreviewed sync proposal marker on line(s) {', '.jo
- {label}: duplicate identity {'/'.join(identity)}
- {label}: dropped or replaced a source from its immutable sna
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/e0cbce18b1182e2e.
Report an issue: GitHub.