chenhg5/cc-connect · critical
codex app-server connection closed: %w
Error message
codex app-server connection closed: %w
What it means
The reader goroutine's scan failed for a reason other than the line-size limit while the session context was still live — the app-server's stdout closed unexpectedly. The library logs the read failure, emits this wrapped error, marks the session dead, and rejects all pending requests and approvals with the same error.
Source
Thrown at agent/codex/appserver_session.go:1028
default:
// Notification (no id).
var notif rpcNotificationEnvelope
if err := json.Unmarshal(data, ¬if); err != nil {
slog.Debug("codex app-server: bad notification envelope", "error", err)
continue
}
s.handleNotification(notif.Method, notif.Params)
}
}
err := scanner.Err()
if err != nil {
if s.ctx.Err() == nil && !errors.Is(err, io.EOF) {
slog.Warn("codex app-server read failed", "error", err)
if errors.Is(err, bufio.ErrTooLong) {
s.emitError(fmt.Errorf("codex app-server line exceeds max size (%d bytes): %w", maxLineSize, err))
} else {
s.emitError(fmt.Errorf("codex app-server connection closed: %w", err))
}
}
s.alive.Store(false)
s.rejectPending(err)
s.rejectPendingApprovals(err)
return
}
s.alive.Store(false)
s.rejectPending(io.EOF)
s.rejectPendingApprovals(io.EOF)
}
func (s *appServerSession) stderrLoop(r io.Reader) {
defer s.wg.Done()
scanner := bufio.NewScanner(r)
buf := make([]byte, 0, 64*1024)
scanner.Buffer(buf, 1024*1024)View on GitHub (pinned to 4000b2338a)
Solutions
- Check codex stderr and system logs (journalctl, dmesg for OOM) for the crash cause
- Restart the session/agent after the process dies — this error is terminal for the session
- Verify codex isn't being killed by resource limits (memory, cgroup)
- Look at the wrapped cause for the actual OS error and fix accordingly (EPIPE → process death)
Example fix
// before
err := sess.Send(ctx, prompt, nil) // fails after process death, session unusable
// after
if err != nil && !sess.IsAlive() {
slog.Warn("codex session died, restarting", "err", err)
sess, err = agent.StartSession(ctx, opts)
} Defensive patterns
Strategy: retry
Validate before calling
if !sess.IsAlive() {
sess, err = agent.StartSession(ctx, opts)
if err != nil { return fmt.Errorf("codex session dead and restart failed: %w", err)
} } Try / catch
if err := sess.Send(ctx, prompt, nil); err != nil {
if !sess.IsAlive() { // connection closed by dead process
if s2, rerr := agent.StartSession(ctx, opts); rerr == nil {
return s2.Send(ctx, prompt, nil)
}
}
return err
} Prevention
- Monitor codex memory usage; raise limits to avoid OOM kills
- Capture stderr to a log file so crash causes are diagnosable
- Watch session Events() for terminal errors and auto-restart the session
- Keep codex updated; crashes often come from fixed bugs
When it happens
Trigger: The codex process dies or closes its stdout pipe mid-session so scanner returns a non-EOF, non-ErrTooLong error (e.g. EPIPE) before s.ctx is cancelled.
Common situations: OOM-killer or crash terminating codex mid-turn; user kills the codex process; pipe broken when the parent reaps the process; container restart; a codex panic writing to stderr and exiting hard.
Understand the failure class
- Connection failures: ECONNREFUSED, ECONNRESET, and friends — why connections get refused, reset, or dropped.
Related errors
- codex app-server exited: %w
- codex app-server turn/start: %w
- %s write timed out
- read stdout: %w
- antigravitySession: stdout pipe: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/8ddee67a24eed1f8.
Report an issue: GitHub.