chenhg5/cc-connect · error
%s
Error message
%s
What it means
When the claude process terminates with an error, finishReadLoop extracts whatever the CLI wrote to stderr and relays it verbatim (fmt.Errorf("%s", stderrMsg)) as a core.EventError on the session event channel. This is the surface through which Claude Code's own failure output (bad flag, auth failure, version mismatch) reaches the engine/user.
Source
Thrown at agent/claudecode/session.go:559
}
_ = stdout.Close()
}()
return waitErrCh, waitDone
}
func (cs *claudeSession) finishReadLoop(waitErrCh <-chan error, stderrBuf *bytes.Buffer) {
err := <-waitErrCh
cs.alive.Store(false)
if err != nil {
stderrMsg := ""
if stderrBuf != nil {
stderrMsg = strings.TrimSpace(stderrBuf.String())
}
if stderrMsg != "" {
slog.Error("claudeSession: process failed", "error", err, "stderr", stderrMsg)
evt := core.Event{Type: core.EventError, Error: fmt.Errorf("%s", stderrMsg)}
select {
case cs.events <- evt:
case <-cs.ctx.Done():
// INVARIANT: readLoop must close cs.events and cs.done exactly once
// on every termination path. Callers (engine event loop) rely on
// these closures to observe session end.
}
}
}
close(cs.events)
close(cs.done)
}
func (cs *claudeSession) handleReadLoopScanErr(err error, waitDone <-chan struct{}) {
if err == nil {
return
}
View on GitHub (pinned to 4000b2338a)
Solutions
- Read the stderr text in the event message — it contains the CLI's own diagnosis and dictates the fix
- Run `claude --version` and `claude doctor`; upgrade or pin the CLI to a version compatible with cc-connect's flags
- Re-authenticate: run `claude login` (or fix ANTHROPIC_API_KEY in providerEnv) as the daemon user
- Reproduce manually with the same args/model from config to see the stderr directly
Defensive patterns
Strategy: try-catch
Validate before calling
// preflight the CLI before creating sessions
func claudeHealthy(bin string) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
out, err := exec.CommandContext(ctx, bin, "--version").CombinedOutput()
if err != nil {
return fmt.Errorf("claude CLI check failed: %v: %s", err, out)
}
return nil
} Try / catch
go func() {
for evt := range sess.Events() {
if evt.Type == core.EventError {
// evt.Error wraps raw CLI stderr — show it and recreate session
notifyUser("claude session failed: " + evt.Error.Error())
sess = recreateSession()
}
}
}() Prevention
- Keep the Claude CLI authenticated (`claude login` as the daemon user; check expiry)
- Pin/verify CLI version compatibility after upgrades — removed flags fail here
- Validate model names in config against the installed CLI
- Alert on EventError events rather than silently ignoring them
When it happens
Trigger: The claude process exits non-zero or the scanner loop ends with an error while stderrBuf holds output — e.g. claude was invoked with an unsupported flag (e.g. after a CLI upgrade removed --replay), the API key/login expired, or the CLI crashed at startup.
Common situations: Expired Claude subscription/login requiring `claude login`; CLI version change breaking a flag; invalid model name in config; network/API outage causing the CLI to abort with a message on stderr.
Related errors
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/a6fdb92b6aeaf74f.
Report an issue: GitHub.