JuliusBrussee/caveman · error · CatalogError
{label}: verified_at is older than 120 days
Error message
{label}: verified_at is older than 120 days What it means
Raised by validate_row() when a row's verified_at is more than 120 days before the validation time. The catalog treats price data as perishable: every row must have been re-checked against the vendor's pricing page within the last 120 days, because prices feed cost accounting and signed receipts (catalogVersion()). This is a staleness gate, not a format check — the timestamp parses fine but has aged out.
Source
Thrown at shared/provider-catalog/validate_catalog.py:166
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"]
if not isinstance(sources, list) or not sources:
raise CatalogError(f"{label}: sources must be a non-empty list")
if any(not isinstance(source, str) or not source.startswith("https://") for source in sources):
raise CatalogError(f"{label}: every source must be HTTPS")
verified_at = parsed_time(row["verified_at"], label)
if verified_at > now:
raise CatalogError(f"{label}: verified_at is in the future")
if verified_at < now - timedelta(days=120):
raise CatalogError(f"{label}: verified_at is older than 120 days")
return identity[0], identity[1], identity[2]
def check_review_markers(text: str, label: str) -> None:
"""Refuse a catalog that still carries an unreviewed sync proposal.
YAML comments are invisible to the parsed rows, so this reads the raw file:
a rubber-stamped merge must not be able to advance price provenance while
the line saying "nobody has confirmed this yet" is still in the file.
"""
offending = [
index + 1
for index, line in enumerate(text.splitlines())
if line.lstrip().startswith(REVIEW_MARKER)
]
if offending:
raise CatalogError(
f"{label}: unreviewed sync proposal marker on line(s) "View on GitHub (pinned to 27d5a3981a)
Solutions
- Re-check the offending row's pricing against its cited provider pricing page, then bump verified_at to the check date AND add/copy the row into the matching catalog/YYYY-MM-DD.yaml snapshot (a new verified_at requires a snapshot that pins it).
- If prices are unchanged, this is still a re-attest: bump verified_at and mint the dated snapshot — you are attesting the numbers still match the vendor page.
- Run validate_catalog.py after editing to confirm the whole pipeline (future check, 120-day check, snapshot pin, sources subset) passes.
Example fix
# before: verified_at older than 120 days verified_at: "2026-03-01T00:00:00Z" # after: re-checked prices on 2026-08-15, snapshot copied to catalog/2026-08-15.yaml verified_at: "2026-08-15T00:00:00Z"
Defensive patterns
Strategy: validation
Validate before calling
from datetime import datetime, timedelta, timezone
def verified_at_is_fresh(verified_at: str, max_age_days: int = 120) -> bool:
ts = datetime.fromisoformat(verified_at.replace("Z", "+00:00")).astimezone(timezone.utc)
return datetime.now(timezone.utc) - ts <= timedelta(days=max_age_days) Try / catch
try:
validate_catalog()
except CatalogError as e:
if "older than 120 days" in str(e):
# schedule a price re-verification pass; bump verified_at + mint snapshot
log_stale_rows(e)
raise Prevention
- Put a recurring (e.g. monthly) calendar task to re-verify catalog prices so rows never approach 120 days.
- Treat the 120-day gate as a hard CI deadline; do not rename dates to pass it.
- When bumping verified_at, always create the matching catalog/<date>.yaml snapshot in the same change.
When it happens
Trigger: validate_catalog() runs (CI gate, pre-commit, or direct python validate_catalog.py) and any row in current.yaml carries verified_at older than now - timedelta(days=120). Typical producers: nobody re-verifying prices for a quarter, an old row surviving model additions, or a reverted re-attest that restored an earlier date (as documented for the 2026-07-30 snapshot removal).
Common situations: A repo that has been quiet for months resumes work and CI fails the catalog gate; a long-lived branch merged after its verification window lapsed; teams that only add new models and never refresh existing rows' price checks.
Related errors
- {label}: verified_at is in the future
- {label}: unreviewed sync proposal marker on line(s) {', '.jo
- {label}: pricing changed without a new verified_at snapshot
- {label}: dropped or replaced a source from its immutable sna
- caveman agent: value is not canonically serializable
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/ab7cb461c8ac8357.
Report an issue: GitHub.