{"record":{"id":"494eddd442d699e5","repo":"JuliusBrussee/caveman","slug":"ccr-typed-object-session-id-is-required","errorCode":null,"errorMessage":"ccr: typed object session_id is required","messagePattern":"ccr: typed object session_id is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/ccr/store.go","lineNumber":106,"sourceCode":"\tOriginalByteLength int         `json:\"original_byte_length\"`\n\tStoredByteLength   int         `json:\"stored_byte_length\"`\n\tData               []byte      `json:\"data\"`\n}\n\nvar objectTypes = map[ObjectType]struct{}{\n\tObjectFileObservation: {}, ObjectSearchResult: {}, ObjectCommandResult: {},\n\tObjectTestResult: {}, ObjectBuildResult: {}, ObjectDiffSnapshot: {},\n\tObjectTaskContract: {}, ObjectTaskDecision: {}, ObjectExecutionState: {},\n\tObjectDocumentationExcerpt: {}, ObjectBrowserSnapshot: {},\n\tObjectRepositoryMap: {}, ObjectEvidenceBundle: {},\n}\n\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}","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/engine/ccr/store.go#L88-L124","documentation":"prepareObject (reached via PutObject) rejects any typed object whose SessionID is blank after trimming. Session identity is mandatory because every CCR object is scoped to a session for retrieval and permission checks. The error fires before any hashing or storage happens.","triggerScenarios":"Calling PutObject(Object{...}) with SessionID omitted or set to whitespace; building objects from deserialized JSON where the session_id field was missing or misnamed (e.g. sessionId vs session_id); test fixtures that only set Type and Data.","commonSituations":"JSON field-name mismatch between a producer using camelCase and the Go struct's `json:\"session_id\"` tag; refactoring code that previously derived sessionID implicitly; copy-pasted test objects missing the field.","solutions":["Set obj.SessionID to the active session identifier before PutObject","If the object arrives from JSON, verify the producer emits `session_id` exactly and the struct tags match","Fail fast at object construction: validate SessionID in your builder/helper so the store never sees it blank"],"exampleFix":"// before\nobj := ccr.Object{Type: ccr.ObjectCommandResult, Data: data}\nid, err := store.PutObject(obj)\n\n// after\nobj := ccr.Object{\n    Type:      ccr.ObjectCommandResult,\n    SessionID: sessionID, // required\n    Data:      data,\n}\nid, err := store.PutObject(obj)","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(obj.SessionID) == \"\" {\n    return errors.New(\"session_id required before PutObject\")\n}\nid, err := store.PutObject(obj)","typeGuard":"func validSessionID(s string) bool { return strings.TrimSpace(s) != \"\" }","tryCatchPattern":null,"preventionTips":["Construct objects through one builder that always stamps the session id","Match JSON tags exactly (session_id) on producers and consumers","Validate at your API boundary, not only at the store"],"tags":["ccr","go","validation","putobject","session"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}