{"record":{"id":"607b2d0d2781c6ec","repo":"JuliusBrussee/caveman","slug":"duplicate-provider-model-region-row-s-s-s","errorCode":null,"errorMessage":"duplicate provider/model/region row %s/%s@%s","messagePattern":"duplicate provider/model/region row (.+?)/(.+?)@(.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/catalog/catalog.go","lineNumber":358,"sourceCode":"\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\n// directory so the catalog resolves when binaries or tests run from subdirs.\n// Both repo layouts are tried: the monorepo keeps the catalog under public/,\n// the published caveman repo has it at the top level.\nfunc catalogCandidates() []string {\n\trels := []string{\n\t\t\"public/shared/provider-catalog/catalog/current.yaml\",\n\t\t\"shared/provider-catalog/catalog/current.yaml\",\n\t}\n\tcandidates := []string{}\n\tif p := os.Getenv(\"CAVE_CATALOG_PATH\"); p != \"\" {","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/catalog/catalog.go#L340-L376","documentation":"DecodeAndValidate enforces uniqueness of catalog rows keyed on provider+model+region (NUL-joined). A second entry with the same triple is rejected to keep the catalog free of ambiguous duplicate rows that would make selection nondeterministic.","triggerScenarios":"Calling DecodeAndValidate on a catalog where two entries share identical provider, model, and region values (e.g. the same row pasted twice, or a regeneration script appending instead of replacing).","commonSituations":"Merging two catalog versions and keeping both copies of a row; a script that appends new entries without deduping; hand-adding a model that already exists in a different region and forgetting to set the region field.","solutions":["Remove the duplicate entry, keeping the most accurate/up-to-date row.","If both rows are genuinely distinct offerings, differentiate their region (or whatever field is accidentally identical).","Add a pre-commit dedup check or JSON-schema uniqueItems-style lint to the catalog pipeline.","Re-run DecodeAndValidate to confirm uniqueness."],"exampleFix":"// before: two rows for openai/gpt-5@us-east\n{\"provider\":\"openai\",\"model\":\"gpt-5\",\"region\":\"us-east\", ...}\n{\"provider\":\"openai\",\"model\":\"gpt-5\",\"region\":\"us-east\", ...}\n// after: single row or distinct regions\n{\"provider\":\"openai\",\"model\":\"gpt-5\",\"region\":\"us-east\", ...}","handlingStrategy":"validation","validationCode":"seen := map[string]struct{}{}\nfor _, e := range entries {\n    k := e.Provider + \"\\x00\" + e.Model + \"\\x00\" + e.Region\n    if _, dup := seen[k]; dup {\n        return fmt.Errorf(\"duplicate row %s/%s@%s\", e.Provider, e.Model, e.Region)\n    }\n    seen[k] = struct{}{}\n}","typeGuard":null,"tryCatchPattern":"decoded, err := catalog.DecodeAndValidate(raw, label)\nif err != nil {\n    if strings.Contains(err.Error(), \"duplicate provider/model/region row\") {\n        return fmt.Errorf(\"deduplicate the catalog: %w\", err)\n    }\n    return err\n}","preventionTips":["Run a dedup pass in the catalog generation script before writing output.","Use a keyed map (provider+model+region) as the canonical storage structure when merging catalogs.","Add a CI check rejecting duplicate rows at PR time."],"tags":["validation","catalog","duplicate"],"backgroundTag":"schema-validation-failed","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}