chenhg5/cc-connect · critical

claudeSession: start: %w

Error message

claudeSession: start: %w

What it means

newClaudeSession wraps the error from cmd.Start() — the `claude` CLI process could not be launched at all. The wrapped error (typically *exec.Error or *fs.PathError) names the root cause: binary not found, non-executable path, bad working directory, or missing environment. A temp prompt file created for this spawn is cleaned up and the context is cancelled before returning.

Source

Thrown at agent/claudecode/session.go:469

	// (下面 startReadLoopWait 对 stdout 已经做了 50ms 后强关来躲这个坑,
	//   注释里还写着 "no descendants holding it" —— 唯独 stderr 漏了同样的处理。)
	//
	// 证据:同一个仓库的 gemini / kimi / antigravity / hooks **四个适配器全都设了
	// WaitDelay**(gemini 那行注释:「确保 I/O goroutine 在 context 结束后不会长时间阻塞」)
	// —— 唯独 claudecode 漏了。这不是新发明,是补上一致性。
	//
	// 取 3s 而非兄弟们的 1s:Claude Code 退出时可能还在吐最后一段 stderr(报错栈),
	// 太短会截掉有用的诊断;而 Close() 的兜底等待是 10s,3s 留足余量。
	// 超时后 Wait() 返回 ErrWaitDelay 并强制关管道 —— stderr 可能不全,
	// 但**进程状态是准的**。这正是要的取舍:宁可少一段日志,不要一个杀不死的会话。
	cmd.WaitDelay = 3 * time.Second

	if err := cmd.Start(); err != nil {
		if promptFilePath != "" && !promptFileIsShared {
			_ = os.Remove(promptFilePath)
		}
		cancel()
		return nil, fmt.Errorf("claudeSession: start: %w", err)
	}

	// Only remember the prompt path for cleanup when it is the per-spawn
	// temp variant. The shared cc-connect-system.md file is reused across
	// all sessions and must never be deleted by an individual session's
	// Close.
	var cleanupPromptPath string
	if !promptFileIsShared {
		cleanupPromptPath = promptFilePath
	}

	cs := &claudeSession{
		cmd:                 cmd,
		stdin:               stdin,
		events:              make(chan core.Event, 64),
		workDir:             workDir,
		ctx:                 sessionCtx,
		cancel:              cancel,

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Verify the binary: run the exact configured path manually (e.g. `claude --version`); fix the binary path in config.toml if wrong
  2. Install/repair Claude Code CLI and ensure it is on PATH for the daemon user (`which claude` as the service user)
  3. Check the wrapped error: exec.ErrNotFound → install binary; 'permission denied' → chmod +x or fix noexec mount; 'fork: cannot allocate memory' → raise limits
  4. If using cc-connect doctor, run it to validate the agent CLI configuration

Example fix

// before (config.toml)
[agents.claudecode]
command = "/usr/local/bin/claude"  # uninstalled after upgrade
// after
[agents.claudecode]
command = "/home/user/.local/bin/claude"
Defensive patterns

Strategy: validation

Validate before calling

// before StartSession, verify the CLI is launchable
bin := "/home/user/.local/bin/claude" // value from config
if _, err := exec.LookPath(bin); err != nil {
    return fmt.Errorf("claude CLI not found: %w", err)
}
if fi, err := os.Stat(bin); err != nil || fi.IsDir() || fi.Mode()&0o111 == 0 {
    return fmt.Errorf("claude CLI missing or not executable: %s", bin)
}

Try / catch

sess, err := agent.StartSession(ctx, prompt)
if err != nil {
    var execErr *exec.Error
    if errors.As(err, &execErr) {
        // binary not found / not executable → surface config guidance to user
        return fmt.Errorf("check 'command' in config.toml: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: StartSession → newClaudeSession → cmd.Start() fails: the configured claude binary path does not exist or is not executable, the working directory is invalid, or env/cgroup limits prevent fork/exec (EAGAIN under memory/pid limits).

Common situations: CLI not installed or not on PATH; config points at a wrong or renamed binary; Claude Code was updated and the path changed; container memory limits block exec; noexec-mounted filesystem for the binary path.

Related errors


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