{"record":{"id":"ce06c837e6ec0323","repo":"JuliusBrussee/caveman","slug":"catalog-is-empty","errorCode":null,"errorMessage":"catalog is empty","messagePattern":"catalog is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/catalog/catalog.go","lineNumber":317,"sourceCode":"// DecodeAndValidate strictly parses a catalog. Unknown YAML fields, duplicate\n// provider/model/region rows, invalid URLs/timestamps, and unsafe prices reject\n// the entire file; runtime must never load a plausible partial catalog.\nfunc DecodeAndValidate(raw []byte) ([]Entry, error) {\n\tdec := yaml.NewDecoder(bytes.NewReader(raw))\n\tdec.KnownFields(true)\n\tvar decoded []Entry\n\tif err := dec.Decode(&decoded); err != nil {\n\t\treturn nil, err\n\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)) {","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/2f49f0e1a352aa810e70056b7930aeb0b3d219b4/shared/platform/catalog/catalog.go#L299-L335","documentation":"DecodeAndValidate rejects an entire YAML catalog that parses to zero entries. The runtime must never load a plausible-but-empty pricing catalog, so callers (load, loadCurrentCatalog, snapshotDrift) fail closed instead of silently operating with no provider/model pricing.","triggerScenarios":"Thrown at shared/platform/catalog/catalog.go:258 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Add at least one valid provider/model/region entry","Check that entries are at the document root as a YAML list and not nested under a stray key","Confirm the file wasn't truncated to just its header"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"2f49f0e1a352aa810e70056b7930aeb0b3d219b4","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}