chenhg5/cc-connect · critical
%s write timed out
Error message
%s write timed out
What it means
Returned by `writeJSONWithTimeout` when writing a JSON-RPC payload to the codex app-server's stdin does not complete within the timeout. The adapter logs a warning and calls `abortTransport()`, killing the child process and closing the session — so this error also tears down the session. The message is `<method> write timed out`.
Source
Thrown at agent/codex/appserver_session.go:1676
}
func (s *appServerSession) writeJSONWithTimeout(method string, v any, timeout time.Duration) error {
done := make(chan error, 1)
go func() {
done <- s.writeJSON(v)
}()
timer := time.NewTimer(timeout)
defer timer.Stop()
ctxDone := s.contextDone()
select {
case err := <-done:
return err
case <-ctxDone:
return s.contextErr()
case <-timer.C:
err := fmt.Errorf("%s write timed out", method)
slog.Warn("codex app-server write timed out, closing session", "method", method, "timeout", timeout)
s.abortTransport()
return err
}
}
func (s *appServerSession) contextDone() <-chan struct{} {
if s.ctx == nil {
return nil
}
return s.ctx.Done()
}
func (s *appServerSession) contextErr() error {
if s.ctx == nil {
return context.Canceled
}
if err := s.ctx.Err(); err != nil {View on GitHub (pinned to 4000b2338a)
Solutions
- Recreate the codex session — `abortTransport` already killed the process, the session is unusable.
- Check whether the codex process hangs on startup (run it manually with the same args).
- Reduce payload size (huge context/instructions) that can fill the pipe buffer.
- Check host resource pressure (CPU, memory, stopped processes).
- Upgrade codex to a version without known stdin-handling deadlocks.
Example fix
// caller side: treat write timeout as fatal for the session
if err := sess.request("turn/create", params, &out); err != nil {
if strings.Contains(err.Error(), "write timed out") {
// before: retry on dead session
// after: rebuild session
sess = newAppServerSession(...)
}
} Defensive patterns
Strategy: fallback
Validate before calling
// check the session is alive and process running before writing
if !sess.Alive() {
return fmt.Errorf("codex session dead; restart before requesting")
} Try / catch
if err := sess.request(method, params, out); err != nil {
if strings.Contains(err.Error(), "write timed out") {
slog.Warn("codex write timed out; session was aborted")
// session is dead — recreate from scratch
return recreateSessionAndRetry(ctx, method, params, out)
}
return err
} Prevention
- Treat any write-timeout error as fatal: the transport was aborted.
- Keep payloads small; huge contexts can fill the stdin pipe.
- Monitor codex process health (cmd.Wait) to detect hangs early.
- Avoid SIGSTOP'd or resource-starved hosts for the codex child.
When it happens
Trigger: `requestWithTimeout` (or notify with timeout) calls `writeJSONWithTimeout`; the goroutine writing to the child's stdin pipe blocks past the deadline because the app-server stopped reading stdin (full pipe, hung process) or the OS pipe is stalled.
Common situations: Codex app-server deadlocked or crashed while the OS pipe buffer is full; stdin buffer exhausted by a large payload; heavy system load stalling the write; child process stopped (SIGSTOP) or PID reaped unexpectedly.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- %s timed out
- codex app-server write: %w
- read stdout: %w
- codex app-server stdin pipe: %w
- codex app-server stdout pipe: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/000abb8ac372d370.
Report an issue: GitHub.