JuliusBrussee/caveman · error · CatalogError

: must be a safe non-empty identifier

Error message

{label}: {field} must be a safe non-empty identifier

What it means

One of provider, model, or region is not a string matching the IDENTIFIER regex `^[A-Za-z0-9][A-Za-z0-9._:@/-]*$` — i.e. not a safe, non-empty, alphanumeric-leading identifier. These three fields form the row's identity (used for duplicate detection and snapshot pinning), so unsafe characters or empty values are rejected. Thrown from validate_row.

Solutions

  1. Use the canonical identifier form: start with a letter/digit, then only letters, digits, and `._:@/-` (e.g. `gpt-4o`, `us-east-1`)
  2. Replace spaces with hyphens and drop or replace disallowed characters
  3. Ensure the value is a non-empty string, not null or a number
  4. Match the exact identifier used elsewhere in the catalog for the same provider/model/region

Example fix

# before
model: "gpt 4o (turbo)"

# after
model: gpt-4o
Defensive patterns

Strategy: validation

Validate before calling

import re
IDENTIFIER = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:@/-]*$")

def identity_fields_ok(row: dict) -> bool:
    return all(isinstance(row.get(f), str) and IDENTIFIER.fullmatch(row[f])
               for f in ("provider", "model", "region"))

Type guard

def is_safe_identifier(value: object) -> bool:
    return isinstance(value, str) and bool(IDENTIFIER.fullmatch(value))

Try / catch

try:
    validate_catalog()
except CatalogError as e:
    if "safe non-empty identifier" in str(e):
        sanitize_identity_field(str(e))

Prevention

When it happens

Trigger: A row has an empty string, a leading special character (`-anthropic`), spaces (`"gpt 4o"`), non-ASCII characters, or a non-string value (null, number) for provider/model/region.

Common situations: Pasting a display name with spaces or uppercase-mangled characters; a script substituting an empty value for a missing env var; using a URL as the provider; quoting quirks leaving a null where a name was expected.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/90257a76afd8403b. Report an issue: GitHub.

Appendix: source

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

        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:
            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

View on GitHub (pinned to 3ee70a1026)