chenhg5/cc-connect · warning
agent session ended
Error message
agent session ended
What it means
Not a returned error but a reason string passed to notifyDroppedQueuedMessages when an interactive session's agent session is missing or no longer Alive() at drain time. All queued user messages for that session are dropped and the user is notified.
Source
Thrown at core/engine.go:3289
// drainOrphanedQueue is called when a message was queued but the drain loop
// has already exited. It processes all pending messages in the state, similar
// to the drain loop in processInteractiveMessageWith but as a standalone
// goroutine.
func (e *Engine) drainOrphanedQueue(session *Session, sessions *SessionManager, interactiveKey string, agent Agent, workspaceDir string) {
unlocked := false
defer func() {
if !unlocked {
session.Unlock()
}
}()
e.interactiveMu.Lock()
state, hasState := e.interactiveStates[interactiveKey]
e.interactiveMu.Unlock()
if !hasState || state == nil || state.agentSession == nil || !state.agentSession.Alive() {
if hasState && state != nil {
e.notifyDroppedQueuedMessages(state, fmt.Errorf("agent session ended"))
}
return
}
// Stop unsolicited reader before draining — drainPendingMessages reads
// from Events() and we must not have concurrent readers.
e.stopUnsolicitedReader(state)
unlocked = e.drainPendingMessages(state, session, sessions, interactiveKey)
// Restart unsolicited reader if the session is still alive and clean.
state.mu.Lock()
alive := state.agentSession != nil && state.agentSession.Alive() && !state.stopped && !state.eventsNeedResync
state.mu.Unlock()
if alive {
e.startUnsolicitedReader(state, session, sessions, interactiveKey, workspaceDir)
}
}View on GitHub (pinned to 4000b2338a)
Solutions
- Check agent process logs for the crash/exit cause
- Recreate the session (/new or send a fresh message) to spawn a new agent session
- Reduce queue time by sending messages when the session is active
- Monitor agent health to detect premature exits
Defensive patterns
Strategy: type-guard
Validate before calling
if state == nil || state.agentSession == nil || !state.agentSession.Alive() { /* recreate session before enqueueing */ } Type guard
alive := state != nil && state.agentSession != nil && state.agentSession.Alive()
Try / catch
engine.OnQueuedMessagesDropped(func(key string, reason error) { user.Notify("session ended; please resend") }) Prevention
- Monitor agent process liveness with doctor checks
- Avoid queuing messages onto sessions showing signs of exit
- Recreate sessions promptly after agent crashes
When it happens
Trigger: Drain runs after the agent process exited/crashed or state.agentSession is nil; interactive state exists but its session is dead.
Common situations: Agent CLI crashed or was killed externally during a long prompt; session timed out and closed before queued messages were flushed.
Related errors
- iflowSession: resolve session dir: %w
- session not found: %s
- session is closed
- session is closed
- session is closed
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/1501289f4e73c97f.
Report an issue: GitHub.