{"record":{"id":"6d665e28029db9e1","repo":"JuliusBrussee/caveman","slug":"native-session-key-read-w","errorCode":null,"errorMessage":"native session key read: %w","messagePattern":"native session key read: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"proxy/internal/nativeruntime/marker.go","lineNumber":58,"sourceCode":"\t\t\t_ = os.Remove(path)\n\t\t\treturn nil, fmt.Errorf(\"native session key write: %w\", writeErr)\n\t\t}\n\t\tif syncErr := file.Sync(); syncErr != nil {\n\t\t\t_ = file.Close()\n\t\t\t_ = os.Remove(path)\n\t\t\treturn nil, fmt.Errorf(\"native session key sync: %w\", syncErr)\n\t\t}\n\t\tif closeErr := file.Close(); closeErr != nil {\n\t\t\treturn nil, fmt.Errorf(\"native session key close: %w\", closeErr)\n\t\t}\n\t\treturn key, nil\n\t}\n\tif !errors.Is(err, os.ErrExist) {\n\t\treturn nil, fmt.Errorf(\"native session key create: %w\", err)\n\t}\n\tkey, err = os.ReadFile(path)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"native session key read: %w\", err)\n\t}\n\tif len(key) != sessionKeyBytes {\n\t\treturn nil, fmt.Errorf(\"native session key length = %d, want %d\", len(key), sessionKeyBytes)\n\t}\n\tif err := os.Chmod(path, 0o600); err != nil {\n\t\treturn nil, fmt.Errorf(\"native session key chmod: %w\", err)\n\t}\n\treturn key, nil\n}\n\n// SessionMarker builds model-temporary correlation context. Local proxy removes\n// valid markers byte-surgically before provider inspection or forwarding.\nfunc SessionMarker(key []byte, sessionID string) (string, error) {\n\tif len(key) != sessionKeyBytes || sessionID == \"\" || len(sessionID) > 256 {\n\t\treturn \"\", errors.New(\"native session marker: invalid key or session id\")\n\t}\n\tencoded := base64.RawURLEncoding.EncodeToString([]byte(sessionID))\n\tsig := markerMAC(key, encoded)","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/internal/nativeruntime/marker.go#L40-L76","documentation":"Another process won the O_EXCL race (ErrExist), but reading the just-created session.key then failed. The file was created by a concurrent startup that is mid-write, was removed between the create attempt and the read, or is unreadable due to permissions. LoadOrCreateSessionKey cannot proceed without the shared key bytes.","triggerScenarios":"Two caveman processes starting simultaneously: the loser of the O_EXCL race reads before the winner finishes writing; antivirus/backup transiently locking the file; another user's 0600 key file read by this user.","commonSituations":"CLI adapter and proxy starting in parallel (the documented shared-key design); shell profiles launching multiple agents at login.","solutions":["Retry startup once after a short delay — the winning process finishes writing 32 bytes almost immediately","If persistent, check ownership/permissions on <home>/runtime/session.key (must be readable by the running user, 0600)","Ensure only one user account uses a given home directory"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"key, err := nativeruntime.LoadOrCreateSessionKey(home)\nif err != nil && strings.Contains(err.Error(), \"session key read\") {\n    time.Sleep(100 * time.Millisecond) // concurrent first-start writer finishes\n    key, err = nativeruntime.LoadOrCreateSessionKey(home)\n}","preventionTips":["Expect the shared-key O_EXCL race when proxy and CLI start together; one retry covers it","Keep one user per home directory so the 0600 key is always readable","Stagger agent autostart in login profiles if races are frequent"],"tags":["filesystem","race","concurrency","nativeruntime","session-key"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}