JuliusBrussee/caveman · error
%s %s/%s: capabilities_verified_at must be RFC3339
Error message
%s %s/%s: capabilities_verified_at must be RFC3339
What it means
Thrown by the catalog YAML loader when the optional capabilities_verified_at field is present but is neither a valid RFC3339 timestamp nor a non-zero time. It mirrors the verified_at format rule: if you record when capabilities were checked, it must be machine-parseable.
Source
Thrown at shared/platform/catalog/catalog.go:282
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)) {
return nil, fmt.Errorf("%s %s/%s: verified_at is in the future", label, entry.Provider, entry.Model)
}
if entry.CapabilitiesVerifiedAt != "" {
capVerified, err := time.Parse(time.RFC3339, entry.CapabilitiesVerifiedAt)
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)
}View on GitHub (pinned to 27d5a3981a)
Solutions
- Use a full RFC3339 value with timezone: capabilities_verified_at: "2026-01-05T10:00:00Z".
- If capabilities were never verified, remove the field entirely (empty string or absence skips the check only when the field is absent/empty as documented).
- Prefer copying the format from verified_at in the same entry.
Example fix
# before capabilities_verified_at: 2026-01-05 # after capabilities_verified_at: 2026-01-05T10:00:00Z
Defensive patterns
Strategy: validation
Validate before calling
if entry.CapabilitiesVerifiedAt != "" {
if t, err := time.Parse(time.RFC3339, entry.CapabilitiesVerifiedAt); err != nil || t.IsZero() {
return fmt.Errorf("capabilities_verified_at must be RFC3339 or absent")
}
} Try / catch
Fail catalog load at startup; correct or remove the field.
Prevention
- Omit capabilities_verified_at entirely when capabilities weren't verified.
- Use the same RFC3339 generator as verified_at.
- Prefer machine-generated catalog edits over manual ones.
When it happens
Trigger: capabilities_verified_at set to "2026-01-05" (no time/timezone), "never", or any unparseable string; an empty string does NOT trigger this (empty means 'not set' and is allowed).
Common situations: Adding the field with a date-only value copied from notes; a schema change where the field became structured but old data kept free-text; hand-editing without noticing the format requirement.
Related errors
- %s %s/%s: verified_at must be RFC3339
- %s %s/%s: verified_at is in the future
- %s %s/%s: capabilities_verified_at is in the future
- ${label}:${lineNo}: "${text}" is not a finite number
- ${label}: row is missing a string "${field}"
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/12abe6dd682f422b.
Report an issue: GitHub.