{"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":110,"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":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/engine/ccr/store.go#L92-L128","documentation":"Validation error from prepareObject in the CCR typed-object path: every typed Object must carry a non-blank SessionID, and unknown/blank values fail closed. PutObject refuses to store the object without it because session scoping is part of the object's identity and retrieval semantics.","triggerScenarios":"Calling PutObject/putObject with Object{...} where SessionID is \"\" or whitespace-only; constructing objects programmatically and forgetting to propagate the session context.","commonSituations":"Background/worker code creating objects without the request's session ID; refactoring that dropped a field from the struct literal; deserializing objects from JSON where session_id was omitted.","solutions":["Set obj.SessionID to the active session's ID before calling PutObject.","Add a constructor/helper that requires sessionID so it cannot be omitted.","If unmarshaling from JSON, validate session_id is present and non-empty before PutObject.","Trim and check strings.TrimSpace(sessionID) != \"\" at the call site."],"exampleFix":"// before\nobj := ccr.Object{Type: ccr.TypeToolResult, Data: data}\nhandle, err := store.PutObject(obj)\n\n// after\nobj := ccr.Object{Type: ccr.TypeToolResult, Data: data, SessionID: session.ID}\nif strings.TrimSpace(obj.SessionID) == \"\" { return errors.New(\"missing session id\") }\nhandle, err := store.PutObject(obj)","handlingStrategy":"validation","validationCode":"func validObject(o ccr.Object) bool {\n    return strings.TrimSpace(o.SessionID) != \"\"\n}","typeGuard":null,"tryCatchPattern":"if err := ensureSessionID(&obj, session.ID); err != nil { return err }\nif _, err := store.PutObject(obj); err != nil {\n    if strings.Contains(err.Error(), \"session_id is required\") {\n        return fmt.Errorf(\"object rejected: %w\", err)\n    }\n    return err\n}","preventionTips":["Construct objects via a helper that takes sessionID as a required parameter.","Validate JSON payloads include session_id before PutObject.","Trim session IDs; reject whitespace-only values early.","Add a unit test asserting PutObject fails on empty SessionID."],"tags":["ccr","validation","missing-field","typed-object"],"backgroundTag":"missing-required-argument","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"}