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

  1. Remove the duplicate entry, keeping the most accurate/up-to-date row.
  2. If both rows are genuinely distinct offerings, differentiate their region (or whatever field is accidentally identical).
  3. Add a pre-commit dedup check or JSON-schema uniqueItems-style lint to the catalog pipeline.
  4. 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

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


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)