JuliusBrussee/caveman · error · CatalogError

{label}: duplicate identity {'/'.join(identity)}

Error message

{label}: duplicate identity {'/'.join(identity)}

What it means

Raised by validate_catalog() when two rows in current.yaml share the same (provider, model, region) identity tuple — the three-part key validate_row() returns and the loop accumulates in `seen`. The catalog is keyed by that tuple; a duplicate makes lookups ambiguous (which row answers a price query), so validation fails fast with the duplicated identity in the message.

Source

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

    identity["price_affecting_capabilities"] = {
        key: capabilities[key] for key in PRICE_AFFECTING_CAPABILITY_KEYS if key in capabilities
    }
    return identity


def validate_catalog(now: datetime | None = None) -> None:
    check_time = now or datetime.now(timezone.utc)
    current_path = CATALOG_DIR / "current.yaml"
    current = load_yaml(current_path)
    check_review_markers(current_path.read_text(encoding="utf-8"), "current.yaml")
    snapshots: dict[str, list[dict[str, Any]]] = {}
    seen: set[tuple[str, str, str]] = set()

    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

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Search current.yaml for the duplicated provider/model/region tuple named in the message and delete or differentiate the redundant row.
  2. If you meant to update pricing, edit the EXISTING row (bumping verified_at and minting a snapshot) instead of adding a second entry.
  3. If regions genuinely differ (e.g. 'us' vs 'global'), fix the region field on the row you intended to change.

Example fix

# before — two identical identities
- {provider: openai, model: gpt-4o, region: us, ...}
- {provider: openai, model: gpt-4o, region: us, ...}  # duplicate

# after — one row, pricing edited in place
- {provider: openai, model: gpt-4o, region: us, pricing: {...updated...}, verified_at: "2026-08-15T00:00:00Z"}
Defensive patterns

Strategy: validation

Validate before calling

def duplicate_identities(rows: list[dict]) -> list[tuple]:
    seen, dupes = set(), []
    for row in rows:
        key = (row.get("provider"), row.get("model"), row.get("region"))
        if key in seen:
            dupes.append(key)
        seen.add(key)
    return dupes

Try / catch

try:
    validate_catalog()
except CatalogError as e:
    if "duplicate identity" in str(e):
        dedupe_by_identity("current.yaml")  # keep/merge into the verified row
    raise

Prevention

When it happens

Trigger: Adding a row for a provider/model/region combination that already exists (e.g. a second 'openai/gpt-4o/us' entry), a sync script appending rather than replacing rows, or a copy-paste edit that forgot to change region or model.

Common situations: Manual catalog edits copying a neighboring row as a template; automated sync tools that append updates instead of upserting; merge conflicts resolved by keeping both sides' rows.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/364aca4d39ed2afc. Report an issue: GitHub.