{"record":{"id":"48a37601eb347db0","repo":"chenhg5/cc-connect","slug":"codex-app-server-write-w","errorCode":null,"errorMessage":"codex app-server write: %w","messagePattern":"codex app-server write: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"agent/codex/appserver_session.go","lineNumber":1744,"sourceCode":"}\n\nfunc (s *appServerSession) writeJSON(v any) error {\n\tb, err := json.Marshal(v)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"codex app-server encode: %w\", err)\n\t}\n\n\ts.procMu.Lock()\n\tstdin := s.stdin\n\ts.procMu.Unlock()\n\tif stdin == nil {\n\t\treturn fmt.Errorf(\"codex app-server connection is closed\")\n\t}\n\n\ts.writeMu.Lock()\n\tdefer s.writeMu.Unlock()\n\tif _, err := stdin.Write(append(b, '\\n')); err != nil {\n\t\treturn fmt.Errorf(\"codex app-server write: %w\", err)\n\t}\n\treturn nil\n}\n","sourceCodeStart":1726,"sourceCodeEnd":1748,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/codex/appserver_session.go#L1726-L1748","documentation":"The actual `stdin.Write` to the codex app-server child process returned an error; the adapter wraps it as `codex app-server write: %w`. The underlying cause is typically EPIPE (process already exited) or `os: file already closed`, meaning the codex process died or its stdin was closed between the nil check and the write.","triggerScenarios":"Writing a JSON-RPC payload to stdin where the OS returns an error: codex process exited (broken pipe), stdin closed concurrently by abortTransport/Stop, or the pipe is full and the write fails non-blockingly.","commonSituations":"Codex crashed mid-session (OOM, panic, auth failure exit); concurrent Stop() while a turn request is in flight; sending turns to a session whose process exited silently; container OOM-killing the codex process.","solutions":["Check the wrapped error: EPIPE/file-closed means the codex process is gone — recreate the session.","Inspect codex's stderr/exit status to find why it died (panic, OOM, bad credentials).","Avoid calling Send concurrently with Stop(); serialize session lifecycle calls.","Add process-exit monitoring (Wait) so sessions are marked dead before writes are attempted.","Check container/host memory limits if the process is being OOM-killed."],"exampleFix":"// before: ignore process exit, keep writing\ngo func() { _ = s.cmd.Wait() }()\n\n// after: mark session dead on exit and fail fast\ngo func() {\n    _ = s.cmd.Wait()\n    s.alive.Store(false)\n    s.rejectPending(fmt.Errorf(\"codex process exited\"))\n}()","handlingStrategy":"try-catch","validationCode":"// verify child process still exists before writing\nif sess.Cmd() == nil || sess.Cmd().Process == nil || !sess.Alive() {\n    return fmt.Errorf(\"codex process not running\")\n}","typeGuard":null,"tryCatchPattern":"if err := sess.request(method, params, out); err != nil {\n    var errno syscall.Errno\n    if errors.As(err, &errno) && (errno == syscall.EPIPE) {\n        slog.Error(\"codex process died (broken pipe); recreating session\")\n        return recreateSession(ctx)\n    }\n    return err\n}","preventionTips":["Wait on the child process and mark the session dead on exit.","Capture codex stderr to diagnose why the process dies.","Check memory limits so codex isn't OOM-killed.","Don't call Send concurrently with Stop()."],"tags":["codex","ipc","pipe","process-crash","write"],"backgroundTag":"broken-pipe","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}