{"record":{"id":"e72c1f84ef9bd820","repo":"JuliusBrussee/caveman","slug":"s-s-s-at-least-one-source-is-required","errorCode":null,"errorMessage":"%s %s/%s: at least one source is required","messagePattern":"(.+?) (.+?)/(.+?): at least one source is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/catalog/catalog.go","lineNumber":289,"sourceCode":"\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 {\n\t\t\treturn nil, fmt.Errorf(\"%s %s/%s: at least one source is required\", label, entry.Provider, entry.Model)\n\t\t}\n\t\tfor _, rawURL := range entry.Sources {\n\t\t\tu, err := url.ParseRequestURI(rawURL)\n\t\t\tif err != nil || u.Scheme != \"https\" || u.Host == \"\" {\n\t\t\t\treturn nil, fmt.Errorf(\"%s %s/%s: invalid HTTPS source %q\", label, entry.Provider, entry.Model, rawURL)\n\t\t\t}\n\t\t}\n\t\tkey := entry.Provider + \"\\x00\" + entry.Model + \"\\x00\" + entry.Region\n\t\tif _, duplicate := seen[key]; duplicate {\n\t\t\treturn nil, fmt.Errorf(\"duplicate provider/model/region row %s/%s@%s\", entry.Provider, entry.Model, entry.Region)\n\t\t}\n\t\tseen[key] = struct{}{}\n\t}\n\treturn decoded, nil\n}\n\n// catalogCandidates lists the paths to try, in order: an explicit env override,\n// the deploy-image and CWD-relative locations, then a walk up from the working","sourceCodeStart":271,"sourceCodeEnd":307,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/catalog/catalog.go#L271-L307","documentation":"Thrown by the catalog YAML loader when an entry carries an empty sources list. Every pricing row must cite at least one source URL so numbers are auditable; rows without provenance are rejected at load.","triggerScenarios":"An entry with sources: [] or with the sources key omitted — len(entry.Sources) == 0 fails the check before individual URL validation runs.","commonSituations":"Adding a new model quickly without gathering the pricing page URL; a generator that only emits sources conditionally; deleting a dead link row entirely instead of replacing it.","solutions":["Add at least one source URL under the entry (e.g. the vendor pricing page you verified).","Note each URL must also be an absolute HTTPS URL or you'll hit the follow-on 'invalid HTTPS source' error.","If re-verifying, record the page you actually used at verification time and update verified_at accordingly."],"exampleFix":"# before\nsources: []\n\n# after\nsources:\n  - https://docs.anthropic.com/en/docs/about-claude/pricing","handlingStrategy":"validation","validationCode":"if len(entry.Sources) == 0 {\n    return fmt.Errorf(\"entry %d %s/%s: add at least one HTTPS source URL\", i, entry.Provider, entry.Model)\n}","typeGuard":null,"tryCatchPattern":"Fail catalog load at startup; provenance is mandatory and cannot be defaulted.","preventionTips":["Capture the pricing-page URL at the moment you verify prices.","Add the source in the same edit that adds or updates a price.","CI-load the catalog so source-less rows cannot merge."],"tags":["catalog","yaml","sources","validation"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}