chenhg5/cc-connect · error

codex app-server stdin pipe: %w

Error message

codex app-server stdin pipe: %w

What it means

This error wraps a failure from cmd.StdinPipe() while starting the codex app-server child process. Obtaining the stdin pipe is part of the child-process bootstrap in the codex agent session; if the OS refuses to allocate the pipe (rare, usually fd exhaustion), the session cannot send JSON-RPC requests to the app-server. The library wraps the OS error with context so callers know which pipe stage failed.

Source

Thrown at agent/codex/appserver_session.go:266

	if provider := strings.TrimSpace(s.modelProvider); provider != "" {
		args = append(args, "-c", fmt.Sprintf("model_provider=%q", provider))
	}
	if baseURL := strings.TrimSpace(s.baseURL); baseURL != "" {
		args = append(args, "-c", fmt.Sprintf("openai_base_url=%q", baseURL))
	}
	cmd := exec.CommandContext(s.ctx, "codex", args...)
	cmd.Dir = s.workDir
	env := append([]string(nil), s.extraEnv...)
	if s.codexHome != "" {
		env = append(env, "CODEX_HOME="+s.codexHome)
	}
	if len(env) > 0 {
		cmd.Env = core.MergeEnv(os.Environ(), env)
	}

	stdin, err := cmd.StdinPipe()
	if err != nil {
		return fmt.Errorf("codex app-server stdin pipe: %w", err)
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return fmt.Errorf("codex app-server stdout pipe: %w", err)
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return fmt.Errorf("codex app-server stderr pipe: %w", err)
	}
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("codex app-server start: %w", err)
	}

	s.procMu.Lock()
	s.cmd = cmd
	s.stdin = stdin
	s.procMu.Unlock()

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Raise the file-descriptor limit (ulimit -n 4096 or systemd LimitNOFILE=4096) for the cc-connect process.
  2. Check for fd leaks: lsof -p <pid> | wc -l; ensure closed sessions release pipes.
  3. Restart the daemon to release leaked descriptors as an immediate mitigation.

Example fix

// before
stdin, err := cmd.StdinPipe()
if err != nil {
    return fmt.Errorf("codex app-server stdin pipe: %w", err)
}
// after
// no code change possible to fix EMFILE; ensure enclosing startup retries
// after caller raises RLIMIT_NOFILE:
if err := start(); err != nil && errors.Is(err, syscall.EMFILE) {
    return fmt.Errorf("file descriptor limit reached; raise RLIMIT_NOFILE: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check fd headroom before spawning the app-server
var lim syscall.Rlimit
syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim)
openFDs := countOpenFDs() // e.g. via /proc/self/fd
if openFDs > int(lim.Cur)-64 {
    return errors.New("file descriptor limit nearly exhausted; raise RLIMIT_NOFILE")
}

Try / catch

if err := session.Start(ctx); err != nil {
    var syscallErr *os.SyscallError
    if errors.As(err, &syscallErr) && (errors.Is(err, syscall.EMFILE) || errors.Is(err, syscall.ENFILE)) {
        return fmt.Errorf("fd limit reached; raise RLIMIT_NOFILE: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling StartSession/ensureStarted on the codex agent, which executes the appServerSession process-startup path; cmd.StdinPipe() returns a non-nil error — almost always because the process has exhausted its file-descriptor limit (EMFILE/ENFILE).

Common situations: ulimit -n too low on a host running many sessions; file-descriptor leak in a long-running daemon accumulating child processes; container with a low RLIMIT_NOFILE.

Related errors


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