chenhg5/cc-connect · critical

codex app-server connection is closed

Error message

codex app-server connection is closed

What it means

`writeJSON` finds `s.stdin == nil` under procMu, meaning the child codex process's stdin was closed or never opened — the transport has been aborted (e.g. after a prior write timeout via abortTransport), the process exited, or Start failed. The fixed message `codex app-server connection is closed` is returned for any write attempted on a dead session.

Source

Thrown at agent/codex/appserver_session.go:1738

		"method":  method,
	}
	if params != nil {
		payload["params"] = params
	}
	return s.writeJSON(payload)
}

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. Recreate the session via StartSession — the old one is unrecoverable once stdin is nil.
  2. Check StartSession's returned error before using the session; a failed start leaves no stdin.
  3. Inspect codex stderr/logs to find why the process exited (bad config, missing login).
  4. Serialize session access so calls don't race with Stop/abortTransport.
  5. Verify the codex binary launches successfully standalone with the same arguments.

Example fix

// before: reuse session after failure
sess.Send(msg)

// after: check liveness and rebuild
if !sess.Alive() {
    sess, err = agent.StartSession(ctx, sessionOpts)
    if err != nil { return err }
}
err = sess.Send(msg)
Defensive patterns

Strategy: validation

Validate before calling

// check session liveness before every request
if !sess.Alive() {
    return fmt.Errorf("codex session closed; call StartSession again")
}

Try / catch

err := sess.request(method, params, out)
if err != nil && err.Error() == "codex app-server connection is closed" {
    // unrecoverable — rebuild
    if sess, err = agent.StartSession(ctx, opts); err != nil {
        return err
    }
    return sess.request(method, params, out)
}

Prevention

When it happens

Trigger: Calling `request`, `requestWithTimeout`, or `notify` after the session was aborted (`abortTransport` set stdin=nil), after `Stop()`, after the codex process crashed, or before `Start()` completed successfully.

Common situations: Reusing a session object after a prior `write timed out` error killed it; codex process exited due to bad config/missing auth and callers keep sending turns; race between session teardown and an in-flight request; never calling Start or Start failing silently.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


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