chenhg5/cc-connect · error
session is closed
Error message
session is closed
What it means
iflowSession.Send returns this when the session's `alive` atomic flag is false, meaning the session has already been closed/stopped and cannot accept new prompts. The library throws it as a guard so callers get a clear error instead of writing to a dead PTY process.
Source
Thrown at agent/iflow/session.go:130
if resumeID != "" && resumeID != core.ContinueSession {
s.sessionID.Store(resumeID)
s.sentOnce.Store(true)
}
return s, nil
}
func (s *iflowSession) Send(prompt string, messageID string, images []core.ImageAttachment, files []core.FileAttachment) error {
if len(images) > 0 {
slog.Warn("iflowSession: images are not supported, ignoring")
}
if len(files) > 0 {
filePaths := core.SaveFilesToDisk(s.workDir, messageID, files)
prompt = core.AppendFileRefs(prompt, filePaths)
}
if !s.alive.Load() {
return fmt.Errorf("session is closed")
}
if !s.turnActive.CompareAndSwap(false, true) {
return fmt.Errorf("iflow session is busy")
}
turnCtx, turnCancel := context.WithCancel(s.ctx)
turn := &iflowTurn{
cancel: turnCancel,
startedAt: time.Now(),
mode: s.mode,
pendingTimeout: s.pendingToolTimeout(),
processDone: make(chan struct{}),
pendingToolIDs: make(map[string]struct{}),
pendingTools: make(map[string]iflowToolUse),
seenToolIDs: make(map[string]struct{}),
doneToolIDs: make(map[string]struct{}),
}
View on GitHub (pinned to 4000b2338a)
Solutions
- Check session liveness (or handle the error) before Send and create a new session via Agent.StartSession when the old one is closed
- Ensure callers stop routing messages to a session after Close/Stop and remove it from the session registry
- If the session closed unexpectedly, inspect logs for the iflow process exit cause (see summarizeIFlowError output) and fix the underlying CLI failure
- Guard concurrent teardown with the session's mutex/once so Close is not invoked while a message is in flight
Example fix
// before
err := session.Send(ctx, prompt, nil)
// after
if !session.IsAlive() {
session, err = agent.StartSession(ctx, opts)
}
if err := session.Send(ctx, prompt, nil); err != nil {
if strings.Contains(err.Error(), "session is closed") {
session, err = agent.StartSession(ctx, opts)
err = session.Send(ctx, prompt, nil)
}
} Defensive patterns
Strategy: try-catch
Validate before calling
if session == nil || !session.IsAlive() {
session, err = agent.StartSession(ctx, opts)
} Type guard
func sessionUsable(s core.AgentSession, alive func() bool) bool { return s != nil && alive() } Try / catch
if err := session.Send(ctx, msg, nil); err != nil {
if strings.Contains(err.Error(), "session is closed") {
session, err = agent.StartSession(ctx, opts)
if err == nil { err = session.Send(ctx, msg, nil) }
}
} Prevention
- Remove sessions from the engine registry on Close/Stop so stale references are never used
- Recreate sessions on 'session is closed' instead of retrying the same object
- Log the session close cause to distinguish user-initiated vs crash closes
When it happens
Trigger: Calling Send() on an AgentSession after Close()/Stop() was called, after the underlying iflow CLI process exited and the session was marked not alive, or after the session context was cancelled.
Common situations: Messaging-platform side keeps a stale session reference after the user ran /stop or the agent process crashed; engine routes a new user message to a session that already terminated; race where the process exits between selecting the session and sending.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- session is closed
- iflow session is busy
- iflowSession: resolve session dir: %w
- iflowSession: start pty: %w
- %s
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/95f1d399ed7f526f.
Report an issue: GitHub.