JuliusBrussee/caveman · error

catalog is empty

Error message

catalog is empty

What it means

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.

Source

Thrown at shared/platform/catalog/catalog.go:317

// DecodeAndValidate strictly parses a catalog. Unknown YAML fields, duplicate
// provider/model/region rows, invalid URLs/timestamps, and unsafe prices reject
// the entire file; runtime must never load a plausible partial catalog.
func DecodeAndValidate(raw []byte) ([]Entry, error) {
	dec := yaml.NewDecoder(bytes.NewReader(raw))
	dec.KnownFields(true)
	var decoded []Entry
	if err := dec.Decode(&decoded); err != nil {
		return nil, err
	}
	var trailing any
	if err := dec.Decode(&trailing); err != io.EOF {
		if err == nil {
			return nil, fmt.Errorf("multiple YAML documents are not allowed")
		}
		return nil, err
	}
	if len(decoded) == 0 {
		return nil, fmt.Errorf("catalog is empty")
	}
	seen := make(map[string]struct{}, len(decoded))
	for i, entry := range decoded {
		label := fmt.Sprintf("entry %d", i)
		if strings.TrimSpace(entry.Provider) == "" || strings.TrimSpace(entry.Model) == "" || strings.TrimSpace(entry.Region) == "" {
			return nil, fmt.Errorf("%s: provider, model, and region are required", label)
		}
		if entry.Currency != "USD" {
			return nil, fmt.Errorf("%s %s/%s: unsupported currency %q", label, entry.Provider, entry.Model, entry.Currency)
		}
		if !cost.ValidPrice(entry.Pricing) {
			return nil, fmt.Errorf("%s %s/%s: invalid pricing", label, entry.Provider, entry.Model)
		}
		verified, err := time.Parse(time.RFC3339, entry.VerifiedAt)
		if err != nil || verified.IsZero() {
			return nil, fmt.Errorf("%s %s/%s: verified_at must be RFC3339", label, entry.Provider, entry.Model)
		}
		if verified.After(time.Now().UTC().Add(24 * time.Hour)) {

View on GitHub (pinned to 2f49f0e1a3)

Solutions

  1. Add at least one valid provider/model/region entry
  2. Check that entries are at the document root as a YAML list and not nested under a stray key
  3. Confirm the file wasn't truncated to just its header
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at shared/platform/catalog/catalog.go:258 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of JuliusBrussee/caveman@2f49f0e1a3 (2026-08-18). Data as JSON: /api/errors/ce06c837e6ec0323. Report an issue: GitHub.