{"record":{"id":"66d6362fd9355b0a","repo":"JuliusBrussee/caveman","slug":"ccr-unknown-lifecycle-q","errorCode":null,"errorMessage":"ccr: unknown lifecycle %q","messagePattern":"ccr: unknown lifecycle %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/ccr/store.go","lineNumber":122,"sourceCode":"\nfunc prepareObject(obj Object) (Object, error) {\n\tif _, ok := objectTypes[obj.Type]; !ok {\n\t\treturn Object{}, fmt.Errorf(\"ccr: unknown object type %q\", obj.Type)\n\t}\n\tif strings.TrimSpace(obj.SessionID) == \"\" {\n\t\treturn Object{}, errors.New(\"ccr: typed object session_id is required\")\n\t}\n\tif obj.Currentness == \"\" {\n\t\tobj.Currentness = Current\n\t}\n\tif obj.Currentness != Current && obj.Currentness != Stale && obj.Currentness != Archived {\n\t\treturn Object{}, fmt.Errorf(\"ccr: unknown currentness %q\", obj.Currentness)\n\t}\n\tif obj.Lifecycle == \"\" {\n\t\tobj.Lifecycle = Hot\n\t}\n\tif obj.Lifecycle != Hot && obj.Lifecycle != Warm && obj.Lifecycle != Cold && obj.Lifecycle != LifecycleArchived {\n\t\treturn Object{}, fmt.Errorf(\"ccr: unknown lifecycle %q\", obj.Lifecycle)\n\t}\n\tif obj.CreatedAt.IsZero() {\n\t\tobj.CreatedAt = time.Now().UTC()\n\t} else {\n\t\tobj.CreatedAt = obj.CreatedAt.UTC()\n\t}\n\tdataSum := sha256.Sum256(obj.Data)\n\tcomputedHash := \"sha256:\" + hex.EncodeToString(dataSum[:])\n\tif obj.ContentHash == \"\" {\n\t\tobj.ContentHash = computedHash\n\t} else if obj.ContentHash != computedHash {\n\t\treturn Object{}, errors.New(\"ccr: typed object content_hash does not match data\")\n\t}\n\tif obj.OriginalByteLength == 0 {\n\t\tobj.OriginalByteLength = len(obj.Data)\n\t}\n\tif obj.StoredByteLength == 0 {\n\t\tobj.StoredByteLength = len(obj.Data)","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/engine/ccr/store.go#L104-L140","documentation":"prepareObject (engine/ccr/store.go:122) validates the Object.Lifecycle field before persisting a CCR object via putObject/PutObject. The store only accepts the four enumerated lifecycle values: Hot, Warm, Cold, and LifecycleArchived. Empty strings are defaulted to Hot, but any other non-empty value is rejected with this error. This is a fail-closed enum guard ensuring tiering metadata in the SQLite recovery store is always well-defined.","triggerScenarios":"Calling PutObject (directly or via putObject) with obj.Lifecycle set to a value that is not exactly Hot, Warm, Cold, or LifecycleArchived — e.g. a hand-rolled string cast like Lifecycle(\"hot\"), Lifecycle(\"archive\"), a typo such as Lifecycle(\"Hott\"), or a lifecycle value imported from another subsystem's vocabulary.","commonSituations":"Developers constructing Object structs manually instead of through store helpers, migrating records whose lifecycle was persisted with an older or different enum spelling, or deserializing lifecycle from config/JSON where case differs (\"Hot\" vs \"hot\"). Also occurs when a custom tier value from a fork or newer version is written into an older store build.","solutions":["Set Lifecycle to one of the exported constants: Hot, Warm, Cold, or LifecycleArchived (or leave it empty to default to Hot).","Normalize the incoming value before calling PutObject — trim whitespace and match case exactly against the constants.","If migrating data from another enum vocabulary, map old values to the four supported lifecycle constants before storage.","Check for typos or custom values (e.g. \"archive\" instead of LifecycleArchived) in the code path that builds the Object."],"exampleFix":"// before\nobj := ccr.Object{Type: ccr.ObjectEvidenceBundle, SessionID: \"s1\", Lifecycle: ccr.Lifecycle(\"hot\")}\nstore.PutObject(ctx, obj) // error: ccr: unknown lifecycle \"hot\"\n\n// after\nobj := ccr.Object{Type: ccr.ObjectEvidenceBundle, SessionID: \"s1\", Lifecycle: ccr.Hot}\nstore.PutObject(ctx, obj)","handlingStrategy":"validation","validationCode":"func validLifecycle(l ccr.Lifecycle) bool {\n\treturn l == \"\" || l == ccr.Hot || l == ccr.Warm || l == ccr.Cold || l == ccr.LifecycleArchived\n}\nif !validLifecycle(obj.Lifecycle) {\n\treturn fmt.Errorf(\"refusing to store object with lifecycle %q\", obj.Lifecycle)\n}","typeGuard":"func isValidLifecycle(v ccr.Lifecycle) bool {\n\tswitch v {\n\tcase \"\", ccr.Hot, ccr.Warm, ccr.Cold, ccr.LifecycleArchived:\n\t\treturn true\n\t}\n\treturn false\n}","tryCatchPattern":"obj, err := store.PutObject(ctx, o)\nif err != nil {\n\tvar unknown *fmt.Errorf\n\tif strings.Contains(err.Error(), \"unknown lifecycle\") {\n\t\tlog.Fatalf(\"invalid lifecycle %q: use Hot|Warm|Cold|LifecycleArchived\", o.Lifecycle)\n\t}\n\treturn err\n}","preventionTips":["Always construct Lifecycle values from the exported constants, never string literals.","Omit Lifecycle entirely when you want the Hot default.","Add a compile-time or unit-test assertion enumerating all valid lifecycle constants.","Validate lifecycle values at config-load time, before they reach the store."],"tags":["go","enum","validation","storage"],"backgroundTag":"invalid-enum-value","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}