{"record":{"id":"2b54d31f94774cd7","repo":"JuliusBrussee/caveman","slug":"s-s-s-invalid-pricing","errorCode":null,"errorMessage":"%s %s/%s: invalid pricing","messagePattern":"(.+?) (.+?)/(.+?): invalid pricing","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/catalog/catalog.go","lineNumber":270,"sourceCode":"\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)\n\t\t\t}\n\t\t}\n\t\tif len(entry.Sources) == 0 {","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/catalog/catalog.go#L252-L288","documentation":"Thrown by the catalog YAML loader when an entry's pricing block fails cost.ValidPrice — the structured per-token price fields (input/output/cache etc.) are missing, malformed, or out of range. Validation runs at load so an unusable pricing row never reaches metering.","triggerScenarios":"Entries with an empty/absent pricing block, non-numeric price strings, negative values, or structurally wrong nesting (e.g. pricing written as a flat scalar instead of the expected shape) — anything cost.ValidPrice rejects.","commonSituations":"Adding a model with unknown prices and leaving pricing empty; pasting prices with currency symbols or commas (\"$3.00\", \"3,00\"); schema drift after the pricing struct gained new required fields.","solutions":["Match the pricing structure of a known-good sibling entry in the same file, with numeric per-token values.","Run cost.ValidPrice (or the repo's catalog test/lint) on the edited file before shipping to catch the failing row early.","If prices are genuinely unknown, follow the project's convention for such rows instead of an empty block — check how other unverifiable entries are represented."],"exampleFix":"# before\npricing: {}\n\n# after\npricing:\n  input_per_mtok: 3.0\n  output_per_mtok: 15.0","handlingStrategy":"validation","validationCode":"if !cost.ValidPrice(entry.Pricing) {\n    return fmt.Errorf(\"entry %d %s/%s: pricing block rejected by cost.ValidPrice\", i, entry.Provider, entry.Model)\n}","typeGuard":null,"tryCatchPattern":"Fail catalog load at startup with the entry index; do not meter with unvalidated prices.","preventionTips":["Copy the pricing block shape from a verified sibling entry.","Write prices as plain numbers (no currency symbols or thousand separators).","Run the catalog loader as a lint step in CI."],"tags":["catalog","yaml","pricing","validation"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}