JuliusBrussee/caveman · error
duplicate provider/model/region row
Error message
duplicate provider/model/region row %s/%s@%s
What it means
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.
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.
Example fix
// before: two rows for openai/gpt-5@us-east
{"provider":"openai","model":"gpt-5","region":"us-east", ...}
{"provider":"openai","model":"gpt-5","region":"us-east", ...}
// after: single row or distinct regions
{"provider":"openai","model":"gpt-5","region":"us-east", ...} Defensive patterns
Strategy: validation
Validate before calling
seen := map[string]struct{}{}
for _, e := range entries {
k := e.Provider + "\x00" + e.Model + "\x00" + e.Region
if _, dup := seen[k]; dup {
return fmt.Errorf("duplicate row %s/%s@%s", e.Provider, e.Model, e.Region)
}
seen[k] = struct{}{}
} Try / catch
decoded, err := catalog.DecodeAndValidate(raw, label)
if err != nil {
if strings.Contains(err.Error(), "duplicate provider/model/region row") {
return fmt.Errorf("deduplicate the catalog: %w", err)
}
return err
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- catalog
- contains duplicate key_id
- : currency must be USD
- : dropped or replaced a source from its immutable snapshot…
- : duplicate identity
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/607b2d0d2781c6ec.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/catalog/catalog.go:358
if err != nil || capVerified.IsZero() {
return nil, fmt.Errorf("%s %s/%s: capabilities_verified_at must be RFC3339", label, entry.Provider, entry.Model)
}
if capVerified.After(time.Now().UTC().Add(24 * time.Hour)) {
return nil, fmt.Errorf("%s %s/%s: capabilities_verified_at is in the future", label, entry.Provider, entry.Model)
}
}
if len(entry.Sources) == 0 {
return nil, fmt.Errorf("%s %s/%s: at least one source is required", label, entry.Provider, entry.Model)
}
for _, rawURL := range entry.Sources {
u, err := url.ParseRequestURI(rawURL)
if err != nil || u.Scheme != "https" || u.Host == "" {
return nil, fmt.Errorf("%s %s/%s: invalid HTTPS source %q", label, entry.Provider, entry.Model, rawURL)
}
}
key := entry.Provider + "\x00" + entry.Model + "\x00" + entry.Region
if _, duplicate := seen[key]; duplicate {
return nil, fmt.Errorf("duplicate provider/model/region row %s/%s@%s", entry.Provider, entry.Model, entry.Region)
}
seen[key] = struct{}{}
}
return decoded, nil
}
// catalogCandidates lists the paths to try, in order: an explicit env override,
// the deploy-image and CWD-relative locations, then a walk up from the working
// directory so the catalog resolves when binaries or tests run from subdirs.
// Both repo layouts are tried: the monorepo keeps the catalog under public/,
// the published caveman repo has it at the top level.
func catalogCandidates() []string {
rels := []string{
"public/shared/provider-catalog/catalog/current.yaml",
"shared/provider-catalog/catalog/current.yaml",
}
candidates := []string{}
if p := os.Getenv("CAVE_CATALOG_PATH"); p != "" {View on GitHub (pinned to 3ee70a1026)