chenhg5/cc-connect · critical

codex app-server write: %w

Error message

codex app-server write: %w

What it means

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.

Source

Thrown at agent/codex/appserver_session.go:1744

}

func (s *appServerSession) writeJSON(v any) error {
	b, err := json.Marshal(v)
	if err != nil {
		return fmt.Errorf("codex app-server encode: %w", err)
	}

	s.procMu.Lock()
	stdin := s.stdin
	s.procMu.Unlock()
	if stdin == nil {
		return fmt.Errorf("codex app-server connection is closed")
	}

	s.writeMu.Lock()
	defer s.writeMu.Unlock()
	if _, err := stdin.Write(append(b, '\n')); err != nil {
		return fmt.Errorf("codex app-server write: %w", err)
	}
	return nil
}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Check the wrapped error: EPIPE/file-closed means the codex process is gone — recreate the session.
  2. Inspect codex's stderr/exit status to find why it died (panic, OOM, bad credentials).
  3. Avoid calling Send concurrently with Stop(); serialize session lifecycle calls.
  4. Add process-exit monitoring (Wait) so sessions are marked dead before writes are attempted.
  5. Check container/host memory limits if the process is being OOM-killed.

Example fix

// before: ignore process exit, keep writing
go func() { _ = s.cmd.Wait() }()

// after: mark session dead on exit and fail fast
go func() {
    _ = s.cmd.Wait()
    s.alive.Store(false)
    s.rejectPending(fmt.Errorf("codex process exited"))
}()
Defensive patterns

Strategy: try-catch

Validate before calling

// verify child process still exists before writing
if sess.Cmd() == nil || sess.Cmd().Process == nil || !sess.Alive() {
    return fmt.Errorf("codex process not running")
}

Try / catch

if err := sess.request(method, params, out); err != nil {
    var errno syscall.Errno
    if errors.As(err, &errno) && (errno == syscall.EPIPE) {
        slog.Error("codex process died (broken pipe); recreating session")
        return recreateSession(ctx)
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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