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, &notif); 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

  1. Check codex stderr and system logs (journalctl, dmesg for OOM) for the crash cause
  2. Restart the session/agent after the process dies — this error is terminal for the session
  3. Verify codex isn't being killed by resource limits (memory, cgroup)
  4. 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

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

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/8ddee67a24eed1f8. Report an issue: GitHub.