{"record":{"id":"897b45c7434116c9","repo":"chenhg5/cc-connect","slug":"codex-app-server-connection-is-closed","errorCode":null,"errorMessage":"codex app-server connection is closed","messagePattern":"codex app-server connection is closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"agent/codex/appserver_session.go","lineNumber":1738,"sourceCode":"\t\t\"method\":  method,\n\t}\n\tif params != nil {\n\t\tpayload[\"params\"] = params\n\t}\n\treturn s.writeJSON(payload)\n}\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":1720,"sourceCodeEnd":1748,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/codex/appserver_session.go#L1720-L1748","documentation":"`writeJSON` finds `s.stdin == nil` under procMu, meaning the child codex process's stdin was closed or never opened — the transport has been aborted (e.g. after a prior write timeout via abortTransport), the process exited, or Start failed. The fixed message `codex app-server connection is closed` is returned for any write attempted on a dead session.","triggerScenarios":"Calling `request`, `requestWithTimeout`, or `notify` after the session was aborted (`abortTransport` set stdin=nil), after `Stop()`, after the codex process crashed, or before `Start()` completed successfully.","commonSituations":"Reusing a session object after a prior `write timed out` error killed it; codex process exited due to bad config/missing auth and callers keep sending turns; race between session teardown and an in-flight request; never calling Start or Start failing silently.","solutions":["Recreate the session via StartSession — the old one is unrecoverable once stdin is nil.","Check StartSession's returned error before using the session; a failed start leaves no stdin.","Inspect codex stderr/logs to find why the process exited (bad config, missing login).","Serialize session access so calls don't race with Stop/abortTransport.","Verify the codex binary launches successfully standalone with the same arguments."],"exampleFix":"// before: reuse session after failure\nsess.Send(msg)\n\n// after: check liveness and rebuild\nif !sess.Alive() {\n    sess, err = agent.StartSession(ctx, sessionOpts)\n    if err != nil { return err }\n}\nerr = sess.Send(msg)","handlingStrategy":"validation","validationCode":"// check session liveness before every request\nif !sess.Alive() {\n    return fmt.Errorf(\"codex session closed; call StartSession again\")\n}","typeGuard":null,"tryCatchPattern":"err := sess.request(method, params, out)\nif err != nil && err.Error() == \"codex app-server connection is closed\" {\n    // unrecoverable — rebuild\n    if sess, err = agent.StartSession(ctx, opts); err != nil {\n        return err\n    }\n    return sess.request(method, params, out)\n}","preventionTips":["Always check StartSession's error before using a session.","Never reuse a session after Stop() or any write-timeout error.","Monitor process exit so dead sessions are flagged before writes.","Serialize lifecycle calls (Stop/Send) to avoid races."],"tags":["codex","app-server","session","lifecycle","ipc"],"backgroundTag":"connection-refused","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"}