JuliusBrussee/caveman · error
%s %s/%s: unsupported currency %q
Error message
%s %s/%s: unsupported currency %q
What it means
Thrown by the catalog YAML loader when an entry's currency field is anything other than the exact string "USD". The catalog stores a single supported currency and rejects others at load time so downstream cost math never mixes currencies silently.
Source
Thrown at shared/platform/catalog/catalog.go:267
}
var trailing any
if err := dec.Decode(&trailing); err != io.EOF {
if err == nil {
return nil, fmt.Errorf("multiple YAML documents are not allowed")
}
return nil, err
}
if len(decoded) == 0 {
return nil, fmt.Errorf("catalog is empty")
}
seen := make(map[string]struct{}, len(decoded))
for i, entry := range decoded {
label := fmt.Sprintf("entry %d", i)
if strings.TrimSpace(entry.Provider) == "" || strings.TrimSpace(entry.Model) == "" || strings.TrimSpace(entry.Region) == "" {
return nil, fmt.Errorf("%s: provider, model, and region are required", label)
}
if entry.Currency != "USD" {
return nil, fmt.Errorf("%s %s/%s: unsupported currency %q", label, entry.Provider, entry.Model, entry.Currency)
}
if !cost.ValidPrice(entry.Pricing) {
return nil, fmt.Errorf("%s %s/%s: invalid pricing", label, entry.Provider, entry.Model)
}
verified, err := time.Parse(time.RFC3339, entry.VerifiedAt)
if err != nil || verified.IsZero() {
return nil, fmt.Errorf("%s %s/%s: verified_at must be RFC3339", label, entry.Provider, entry.Model)
}
if verified.After(time.Now().UTC().Add(24 * time.Hour)) {
return nil, fmt.Errorf("%s %s/%s: verified_at is in the future", label, entry.Provider, entry.Model)
}
if entry.CapabilitiesVerifiedAt != "" {
capVerified, err := time.Parse(time.RFC3339, entry.CapabilitiesVerifiedAt)
if err != nil || capVerified.IsZero() {
return nil, fmt.Errorf("%s %s/%s: capabilities_verified_at must be RFC3339", label, entry.Provider, entry.Model)
}
if capVerified.After(time.Now().UTC().Add(24 * time.Hour)) {
return nil, fmt.Errorf("%s %s/%s: capabilities_verified_at is in the future", label, entry.Provider, entry.Model)View on GitHub (pinned to 27d5a3981a)
Solutions
- Set currency: USD (uppercase) on the offending entry.
- If the source price is in another currency, convert the price to USD first and then record currency: USD with a source URL documenting the conversion.
- Keep the field present on every entry — there is no default.
Example fix
# before
- provider: mistral
model: mistral-large
region: eu
currency: EUR
pricing: {...}
# after
- provider: mistral
model: mistral-large
region: eu
currency: USD
pricing: {...} # converted per-token prices Defensive patterns
Strategy: validation
Validate before calling
if entry.Currency != "USD" {
return fmt.Errorf("entry %d: currency must be USD (got %q); convert the price first", i, entry.Currency)
} Try / catch
Fail catalog load at startup; converting currencies per-row at runtime is not supported, so the data must be fixed.
Prevention
- Standardize the currency: USD line in catalog templates and snippets.
- If importing non-USD prices, convert once at authoring time and cite the FX source in the sources list.
- CI-load the catalog so currency typos (usd, USD. etc.) break the build, not boot.
When it happens
Trigger: An entry with currency: "usd" (case-sensitive check), currency: "EUR", or a missing/renamed field that decodes to "" — any value != "USD".
Common situations: Hand-editing a new entry and typing lowercase 'usd'; importing pricing from a European provider page denominated in EUR; omitting the currency field on a new row (defaults to empty string).
Related errors
- ${label}: ${key} has no pricing block
- %s: provider, model, and region are required
- %s %s/%s: invalid pricing
- ${label}:${lineNo}: "${text}" is not a finite number
- ${label}: row is missing a string "${field}"
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/f35a66186ed4483f.
Report an issue: GitHub.