chenhg5/cc-connect · error

geminiSession: stdout pipe: %w

Error message

geminiSession: stdout pipe: %w

What it means

Send builds the exec.Cmd for the gemini CLI and calls StdoutPipe(); if the OS pipe cannot be created it wraps the error as `geminiSession: stdout pipe: %w` and aborts the send. This is a rare OS-level failure creating the unidirectional pipe for capturing CLI output.

Source

Thrown at agent/gemini/session.go:187

			cancel()
		}
	}()

	slog.Debug("geminiSession: launching", "resume", isResume, "args", core.RedactArgs(args))
	cmd := exec.CommandContext(ctx, gs.cmd, args...)
	// Set a short WaitDelay to ensure I/O goroutines don't block for long after the context is done
	cmd.WaitDelay = 1 * time.Second
	cmd.Dir = gs.workDir
	env := os.Environ()
	if len(gs.extraEnv) > 0 {
		env = core.MergeEnv(env, gs.extraEnv)
	}
	cmd.Env = env
	cmd.Stdin = strings.NewReader(fullPrompt)

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return fmt.Errorf("geminiSession: stdout pipe: %w", err)
	}

	var stderrBuf bytes.Buffer
	cmd.Stderr = &stderrBuf

	if err := cmd.Start(); err != nil {
		return fmt.Errorf("geminiSession: start: %w", err)
	}

	started = true
	gs.wg.Add(1)
	go func() {
		defer cancel()
		gs.readLoop(ctx, cmd, stdout, &stderrBuf, append(imageRefs, fileRefs...))
	}()

	return nil
}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Check fd usage of the cc-connect process (`ls /proc/<pid>/fd | wc -l`) and raise the limit (`ulimit -n` / LimitNOFILE in systemd)
  2. Look for leaked gemini child processes or unclosed sessions causing fd exhaustion
  3. Restart the daemon to clear leaked descriptors
  4. Ensure each exec.Cmd is used once (fresh Cmd per Send)

Example fix

// before
[Service]
ExecStart=/usr/local/bin/cc-connect
// after
[Service]
LimitNOFILE=65536
ExecStart=/usr/local/bin/cc-connect
Defensive patterns

Strategy: retry

Validate before calling

f, err := os.Open(os.DevNull); if err == nil { f.Close() } else { return fmt.Errorf("fd exhaustion likely: %w", err) }

Try / catch

if err := sess.Send(prompt, id, nil, nil); err != nil && strings.Contains(err.Error(), "stdout pipe") {
  time.Sleep(500 * time.Millisecond)
  return sess.Send(prompt, id, nil, nil) // retry once; persistent failure => fd leak
}

Prevention

When it happens

Trigger: Calling Send when cmd.StdoutPipe() fails — typically fd exhaustion (too many open files) or an internal os/exec state problem (e.g. Stdout already set / command reused).

Common situations: Daemon leaking processes/fds over a long uptime hitting the RLIMIT_NOFILE limit; misconfigured ulimits in containers; reusing an exec.Cmd that already had StdoutPipe called.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


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