{"record":{"id":"73004008596034a7","repo":"JuliusBrussee/caveman","slug":"native-runtime-session-id-is-required","errorCode":null,"errorMessage":"native runtime: session id is required","messagePattern":"native runtime: session id is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/internal/nativeruntime/runtime.go","lineNumber":216,"sourceCode":"\t\treturn profile, profileFeatures{taskContract: true, compactState: true, reuse: true, capture: true, mask: true, repository: true}, nil\n\tdefault:\n\t\treturn \"\", profileFeatures{}, fmt.Errorf(\"native runtime: unknown profile %q\", raw)\n\t}\n}\n\nfunc (r *Runtime) Handle(_ context.Context, request Request) (Response, error) {\n\tstarted := time.Now()\n\tr.mu.Lock()\n\tdefer r.mu.Unlock()\n\n\tif request.ProtocolVersion != ProtocolVersion {\n\t\treturn Response{}, fmt.Errorf(\"native runtime: unsupported protocol version %d\", request.ProtocolVersion)\n\t}\n\tif _, ok := eventTypes[request.Event.Type]; !ok {\n\t\treturn Response{}, fmt.Errorf(\"native runtime: unknown event %q\", request.Event.Type)\n\t}\n\tif strings.TrimSpace(request.Session.ID) == \"\" {\n\t\treturn Response{}, errors.New(\"native runtime: session id is required\")\n\t}\n\tpolicyMode := request.PolicyMode\n\tif policyMode == \"\" {\n\t\tpolicyMode = \"safe\"\n\t}\n\tif policyMode != \"record\" && policyMode != \"safe\" && policyMode != \"max\" {\n\t\treturn Response{}, fmt.Errorf(\"native runtime: unknown policy mode %q\", policyMode)\n\t}\n\trequest.PolicyMode = policyMode\n\tprofile, features, err := resolveProfile(request.Profile, policyMode)\n\tif err != nil {\n\t\treturn Response{}, err\n\t}\n\trequest.Profile = profile\n\tr.lastActivity = time.Now()\n\tif request.Event.Type == \"session.end\" {\n\t\tdelete(r.activeSessions, request.Session.ID)\n\t} else {","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/internal/nativeruntime/runtime.go#L198-L234","documentation":"Runtime.Handle validates each request before processing; a request whose Session.ID is blank after trimming is rejected because all runtime state (decisions, CCR objects, receipts) is keyed by session. This check runs after protocol-version and event-type validation, under the runtime's mutex.","triggerScenarios":"Sending a Request over the unix socket/named pipe with session.id omitted, empty, or whitespace; a client struct where the json tag for the session id doesn't match the protocol field name.","commonSituations":"First-request bootstrapping code that hasn't allocated a session yet; JSON field-name mismatch (sessionId vs session) in a hand-rolled client; tests constructing Request literals without Session.","solutions":["Populate request.Session.ID with a stable non-empty session identifier before sending","Check the client's JSON tags against the protocol's `session.id` path","Allocate/persist a session id before the first Handle call and reuse it for the session's lifetime"],"exampleFix":"// before\nresp, err := rt.Handle(ctx, nativeruntime.Request{Event: evt}) // Session.ID empty\n\n// after\nresp, err := rt.Handle(ctx, nativeruntime.Request{\n    Session: nativeruntime.Session{ID: sessionID},\n    Event:   evt,\n})","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(request.Session.ID) == \"\" {\n    return errors.New(\"allocate a session id before Handle\")\n}\nresp, err := rt.Handle(ctx, request)","typeGuard":"func hasSession(r nativeruntime.Request) bool {\n    return strings.TrimSpace(r.Session.ID) != \"\"\n}","tryCatchPattern":null,"preventionTips":["Allocate and persist the session id before the first request","Match client JSON field names to the protocol (session.id)"],"tags":["nativeruntime","go","validation","session","request"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}