{"record":{"id":"8f76909040055e94","repo":"chenhg5/cc-connect","slug":"read-stdout-w-8f7690","errorCode":null,"errorMessage":"read stdout: %w","messagePattern":"read stdout: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/codex/session.go","lineNumber":338,"sourceCode":"\tif err := readJSONLines(stdout, func(line []byte) error {\n\t\tlineText := string(line)\n\t\tif lineText == \"\" {\n\t\t\treturn nil\n\t\t}\n\n\t\tslog.Debug(\"codexSession: raw\", \"line\", truncate(lineText, 500))\n\n\t\tvar raw map[string]any\n\t\tif err := json.Unmarshal(line, &raw); err != nil {\n\t\t\tslog.Debug(\"codexSession: non-JSON line\", \"line\", lineText)\n\t\t\treturn nil\n\t\t}\n\n\t\tcs.handleEvent(raw)\n\t\treturn nil\n\t}); err != nil {\n\t\tslog.Error(\"codexSession: read stdout error\", \"error\", err)\n\t\tevt := core.Event{Type: core.EventError, Error: fmt.Errorf(\"read stdout: %w\", err)}\n\t\tselect {\n\t\tcase cs.events <- evt:\n\t\tcase <-cs.ctx.Done():\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc readJSONLines(r io.Reader, handle func([]byte) error) error {\n\treader := bufio.NewReader(r)\n\n\tfor {\n\t\tline, err := reader.ReadBytes('\\n')\n\t\tif errors.Is(err, io.EOF) && len(line) == 0 {\n\t\t\treturn nil\n\t\t}\n\t\tif err != nil && !errors.Is(err, io.EOF) {\n\t\t\treturn err","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/codex/session.go#L320-L356","documentation":"The Codex session's readLoop goroutine scans the agent process's stdout with a line scanner; when that scanner returns an error (process exit, closed pipe, I/O failure), it logs it and emits a core.EventError wrapping the underlying error. This is how the session surfaces that the Codex app-server's stdout stream broke. Note that a normal process exit produces EOF here, so this often appears after the agent process dies unexpectedly.","triggerScenarios":"The codex process terminates or crashes while a session is active; the stdout pipe is closed or broken; an I/O read error occurs on the scanner (bufio.Scanner/ReadString failure) inside readLoop.","commonSituations":"The codex binary segfaults or is OOM-killed mid-session; the user's shell/environment kills the child process; codex exits early due to bad flags or auth so stdout closes while readLoop is still scanning; system resource limits break the pipe.","solutions":["Check that the codex CLI binary is on PATH, executable, and runs (`codex --version`) — early exit is the most common cause.","Verify codex authentication (`codex login` / auth config) so the process does not die immediately after startup.","Inspect session logs for the preceding `codexSession: read stdout error` slog entry and any stderr output from the process to find the root exit cause.","If the session is stale, create a new session via StartSession; the events channel is closed/drained after this error.","Check system resource limits (ulimit, memory) if the process is being killed under load."],"exampleFix":"// before: process exits due to missing auth and stdout EOF surfaces as an opaque error\n// after: verify the binary works and is authenticated before creating a session\nif err := exec.Command(\"codex\", \"login\", \"status\").Run(); err != nil {\n    return fmt.Errorf(\"codex not authenticated: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"// before creating a session\ncmd := exec.Command(\"codex\", \"--version\")\nif err := cmd.Run(); err != nil {\n    return fmt.Errorf(\"codex binary not runnable: %w\", err)\n}","typeGuard":"func isStdoutReadError(evt core.Event) bool {\n    return evt.Type == core.EventError && evt.Error != nil && strings.Contains(evt.Error.Error(), \"read stdout:\")\n}","tryCatchPattern":"go func() {\n    for evt := range sess.Events() {\n        if evt.Type == core.EventError && strings.Contains(evt.Error.Error(), \"read stdout:\") {\n            slog.Warn(\"codex stream broke, recreating session\", \"err\", evt.Error)\n            // fall back: recreate session and resend pending message\n        }\n    }\n}()","preventionTips":["Pre-flight check the codex binary and authentication before opening sessions","Monitor stderr/logs for early process exit causes","Set resource limits (ulimit, memory) appropriate for long-lived child processes","Handle the error event by recreating the session instead of reusing the dead one"],"tags":["codex","stdout","process-lifecycle","stream-read"],"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"}