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
- Check the wrapped error: EPIPE/file-closed means the codex process is gone — recreate the session.
- Inspect codex's stderr/exit status to find why it died (panic, OOM, bad credentials).
- Avoid calling Send concurrently with Stop(); serialize session lifecycle calls.
- Add process-exit monitoring (Wait) so sessions are marked dead before writes are attempted.
- 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
- Wait on the child process and mark the session dead on exit.
- Capture codex stderr to diagnose why the process dies.
- Check memory limits so codex isn't OOM-killed.
- Don't call Send concurrently with Stop().
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
- %s write timed out
- %s timed out
- codex app-server connection is closed
- runtime config stdin pipe: %w
- runtime config stdout pipe: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/48a37601eb347db0.
Report an issue: GitHub.