{"record":{"id":"6b3a454914e78bc2","repo":"JuliusBrussee/caveman","slug":"s-s-s-verified-at-must-be-rfc3339","errorCode":null,"errorMessage":"%s %s/%s: verified_at must be RFC3339","messagePattern":"(.+?) (.+?)/(.+?): verified_at must be RFC3339","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/catalog/catalog.go","lineNumber":274,"sourceCode":"\t}\n\tif len(decoded) == 0 {\n\t\treturn nil, fmt.Errorf(\"catalog is empty\")\n\t}\n\tseen := make(map[string]struct{}, len(decoded))\n\tfor i, entry := range decoded {\n\t\tlabel := fmt.Sprintf(\"entry %d\", i)\n\t\tif strings.TrimSpace(entry.Provider) == \"\" || strings.TrimSpace(entry.Model) == \"\" || strings.TrimSpace(entry.Region) == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"%s: provider, model, and region are required\", label)\n\t\t}\n\t\tif entry.Currency != \"USD\" {\n\t\t\treturn nil, fmt.Errorf(\"%s %s/%s: unsupported currency %q\", label, entry.Provider, entry.Model, entry.Currency)\n\t\t}\n\t\tif !cost.ValidPrice(entry.Pricing) {\n\t\t\treturn nil, fmt.Errorf(\"%s %s/%s: invalid pricing\", label, entry.Provider, entry.Model)\n\t\t}\n\t\tverified, err := time.Parse(time.RFC3339, entry.VerifiedAt)\n\t\tif err != nil || verified.IsZero() {\n\t\t\treturn nil, fmt.Errorf(\"%s %s/%s: verified_at must be RFC3339\", label, entry.Provider, entry.Model)\n\t\t}\n\t\tif verified.After(time.Now().UTC().Add(24 * time.Hour)) {\n\t\t\treturn nil, fmt.Errorf(\"%s %s/%s: verified_at is in the future\", label, entry.Provider, entry.Model)\n\t\t}\n\t\tif entry.CapabilitiesVerifiedAt != \"\" {\n\t\t\tcapVerified, err := time.Parse(time.RFC3339, entry.CapabilitiesVerifiedAt)\n\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)","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/catalog/catalog.go#L256-L292","documentation":"Thrown by the catalog YAML loader when an entry's verified_at timestamp cannot be parsed as RFC3339, or parses to the zero time. Each price row must carry a machine-parseable verification timestamp so consumers can judge staleness; free-text or wrongly formatted dates are rejected at load.","triggerScenarios":"verified_at values like \"2026-01-05\" (date only), \"Jan 5, 2026\", \"2026-01-05T10:00:00\" (no timezone), or a missing field (empty string) — all fail time.Parse(time.RFC3339, ...) or produce a zero time.","commonSituations":"Hand-entering human-style dates; omitting the timezone offset; a scraper writing ISO dates without the 'T' or the +00:00 suffix.","solutions":["Use a full RFC3339 timestamp with timezone, e.g. verified_at: \"2026-01-05T10:00:00Z\".","Generate timestamps programmatically (time.Now().UTC().Format(time.RFC3339)) instead of typing them.","Remember the same format is enforced for capabilities_verified_at when that optional field is present."],"exampleFix":"# before\nverified_at: 2026-01-05\n\n# after\nverified_at: 2026-01-05T10:00:00Z","handlingStrategy":"validation","validationCode":"const rfc3339RE = `^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$`\n\nfunc validRFC3339(s string) bool {\n    t, err := time.Parse(time.RFC3339, s)\n    return err == nil && !t.IsZero()\n}","typeGuard":null,"tryCatchPattern":"Fail catalog load at startup; the timestamp must be corrected in the YAML.","preventionTips":["Generate timestamps with date -u +%Y-%m-%dT%H:%M:%SZ, never type them.","Always include the timezone (Z or +HH:MM).","Lint both verified_at and capabilities_verified_at with time.Parse(time.RFC3339, ...) in CI."],"tags":["catalog","yaml","timestamp","rfc3339","validation"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}