{"record":{"id":"f35a66186ed4483f","repo":"JuliusBrussee/caveman","slug":"s-s-s-unsupported-currency-q","errorCode":null,"errorMessage":"%s %s/%s: unsupported currency %q","messagePattern":"(.+?) (.+?)/(.+?): unsupported currency %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/catalog/catalog.go","lineNumber":267,"sourceCode":"\t}\n\tvar trailing any\n\tif err := dec.Decode(&trailing); err != io.EOF {\n\t\tif err == nil {\n\t\t\treturn nil, fmt.Errorf(\"multiple YAML documents are not allowed\")\n\t\t}\n\t\treturn nil, err\n\t}\n\tif len(decoded) == 0 {\n\t\treturn nil, fmt.Errorf(\"catalog is empty\")\n\t}\n\tseen := make(map[string]struct{}, len(decoded))\n\tfor i, entry := range decoded {\n\t\tlabel := fmt.Sprintf(\"entry %d\", i)\n\t\tif strings.TrimSpace(entry.Provider) == \"\" || strings.TrimSpace(entry.Model) == \"\" || strings.TrimSpace(entry.Region) == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"%s: provider, model, and region are required\", label)\n\t\t}\n\t\tif entry.Currency != \"USD\" {\n\t\t\treturn nil, fmt.Errorf(\"%s %s/%s: unsupported currency %q\", label, entry.Provider, entry.Model, entry.Currency)\n\t\t}\n\t\tif !cost.ValidPrice(entry.Pricing) {\n\t\t\treturn nil, fmt.Errorf(\"%s %s/%s: invalid pricing\", label, entry.Provider, entry.Model)\n\t\t}\n\t\tverified, err := time.Parse(time.RFC3339, entry.VerifiedAt)\n\t\tif err != nil || verified.IsZero() {\n\t\t\treturn nil, fmt.Errorf(\"%s %s/%s: verified_at must be RFC3339\", label, entry.Provider, entry.Model)\n\t\t}\n\t\tif verified.After(time.Now().UTC().Add(24 * time.Hour)) {\n\t\t\treturn nil, fmt.Errorf(\"%s %s/%s: verified_at is in the future\", label, entry.Provider, entry.Model)\n\t\t}\n\t\tif entry.CapabilitiesVerifiedAt != \"\" {\n\t\t\tcapVerified, err := time.Parse(time.RFC3339, entry.CapabilitiesVerifiedAt)\n\t\t\tif err != nil || capVerified.IsZero() {\n\t\t\t\treturn nil, fmt.Errorf(\"%s %s/%s: capabilities_verified_at must be RFC3339\", label, entry.Provider, entry.Model)\n\t\t\t}\n\t\t\tif capVerified.After(time.Now().UTC().Add(24 * time.Hour)) {\n\t\t\t\treturn nil, fmt.Errorf(\"%s %s/%s: capabilities_verified_at is in the future\", label, entry.Provider, entry.Model)","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/catalog/catalog.go#L249-L285","documentation":"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.","triggerScenarios":"An entry with currency: \"usd\" (case-sensitive check), currency: \"EUR\", or a missing/renamed field that decodes to \"\" — any value != \"USD\".","commonSituations":"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).","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."],"exampleFix":"# before\n- provider: mistral\n  model: mistral-large\n  region: eu\n  currency: EUR\n  pricing: {...}\n\n# after\n- provider: mistral\n  model: mistral-large\n  region: eu\n  currency: USD\n  pricing: {...}  # converted per-token prices","handlingStrategy":"validation","validationCode":"if entry.Currency != \"USD\" {\n    return fmt.Errorf(\"entry %d: currency must be USD (got %q); convert the price first\", i, entry.Currency)\n}","typeGuard":null,"tryCatchPattern":"Fail catalog load at startup; converting currencies per-row at runtime is not supported, so the data must be fixed.","preventionTips":["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."],"tags":["catalog","yaml","pricing","currency","validation"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}