chenhg5/cc-connect · error
%s
Error message
%s
What it means
agent/pi/session.go:430 — after a JSON-mode turn completes, sendJSON flushes any deferred terminal error stored in s.pendingErr as core.Event{Type:EventError, Error:fmt.Errorf("%s", s.pendingErr)}. pendingErr is set by handleEvent (e.g. a non-retryable agent error that didn't end with a failed agent_end); this flush covers the case where the process exited without a final agent_end to flush it normally.
Source
Thrown at agent/pi/session.go:430
}
s.handleEvent(raw)
}
err = cmd.Wait()
if err != nil {
slog.Error("piSession: process error", "cmd", s.cmd, "error", err, "stderr", stderrBuf.String())
evt := core.Event{Type: core.EventError, Error: fmt.Errorf("pi: %s: %w", strings.TrimSpace(stderrBuf.String()), err)}
select {
case s.events <- evt:
case <-s.ctx.Done():
}
}
// Signal turn completion. Flush a deferred terminal error first in
// case the process exited without a final non-retry agent_end (the
// agent_end handler normally flushes pendingErr already).
if s.pendingErr != "" {
errEvt := core.Event{Type: core.EventError, Error: fmt.Errorf("%s", s.pendingErr)}
s.pendingErr = ""
select {
case s.events <- errEvt:
case <-s.ctx.Done():
}
}
sid := s.CurrentSessionID()
evt := core.Event{Type: core.EventResult, SessionID: sid, Done: true}
select {
case s.events <- evt:
case <-s.ctx.Done():
}
return nil
}
// writeRPCCommand marshals cmd as a single JSONL line and writes it to the
// RPC process's stdin under rpcStdinMu. Used by both sendRPC (for "prompt"View on GitHub (pinned to 4000b2338a)
Solutions
- Read the pendingErr text in the event — it is pi's terminal error message; address what it describes (auth, quota, prompt size).
- Retry the prompt if the cause was transient (rate limit, network).
- Shorten the prompt or move large content into on-disk files referenced by path rather than inlined.
- Check pi logs/version if the message indicates an internal pi error and upgrade.
Defensive patterns
Strategy: fallback
Type guard
func isPendingErrFlush(evt core.Event) bool {
return evt.Type == core.EventError && evt.Error != nil &&
!strings.HasPrefix(evt.Error.Error(), "pi: ") &&
!strings.HasPrefix(evt.Error.Error(), "read stdout:")
} Try / catch
sawResult := false
for evt := range session.Events() {
if evt.Type == core.EventResult && evt.Done { sawResult = true }
if evt.Type == core.EventError && evt.Error != nil {
slog.Warn("pi terminal agent error", "msg", evt.Error.Error())
}
}
if !sawResult { // turn ended via pendingErr flush, not agent_end
offerRetryToUser()
} Prevention
- Treat EventError without a following Done result as a failed turn and offer a retry.
- Keep provider credentials and quota healthy to avoid terminal agent errors.
- Shorten prompts and reference large files by path to avoid provider rejections.
- Log pendingErr text verbatim for diagnosis; it names pi's failure reason.
When it happens
Trigger: sendJSON (via Send, non-rpc mode) when handleEvent recorded s.pendingErr during the turn: (1) pi reported a fatal, non-retryable agent error event; (2) the process exited before emitting a terminal agent_end that would normally clear it; (3) a provider refused the request with a terminal status pi surfaced as pending error text.
Common situations: Provider auth/quota failures mid-turn; pi aborting on an invalid tool call; context-window overflow errors from the model; prompt rejected by the provider (e.g. oversized inlined content).
Related errors
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/130fcb4cc803907e.
Report an issue: GitHub.