chenhg5/cc-connect · error
session is closed
Error message
session is closed
What it means
Send in agent/qoder/session.go rejects any message when the session's atomic alive flag is false, returning the fixed error "session is closed". The qoder session is one-shot: Send starts a new qoder process per message, and once Close() runs (or the session was never kept alive) further sends are invalid state transitions.
Source
Thrown at agent/qoder/session.go:92
}
if resumeID != "" && resumeID != core.ContinueSession {
qs.sessionID.Store(resumeID)
}
return qs, nil
}
func (qs *qoderSession) Send(prompt string, messageID string, images []core.ImageAttachment, files []core.FileAttachment) error {
if len(images) > 0 {
slog.Warn("qoderSession: images not supported, ignoring")
}
if len(files) > 0 {
filePaths := core.SaveFilesToDisk(qs.workDir, messageID, files)
prompt = core.AppendFileRefs(prompt, filePaths)
}
if !qs.alive.Load() {
return fmt.Errorf("session is closed")
}
args := append(append([]string{}, qs.extraArgs...), "-p", prompt, "-f", "stream-json", "-q", "-w", qs.workDir)
sid := qs.CurrentSessionID()
if sid != "" {
args = append(args, "-r", sid)
}
if qs.mode == "yolo" {
if os.Geteuid() == 0 {
slog.Warn("qoderSession: --dangerously-skip-permissions not allowed under root, skipping flag")
} else {
args = append(args, "--dangerously-skip-permissions")
}
}
if qs.model != "" {View on GitHub (pinned to 4000b2338a)
Solutions
- Check sess.Alive() before calling Send and create a new session if false
- Ensure the engine creates a fresh session after Close instead of reusing the old one
- Avoid racing Send with Close; serialize lifecycle transitions
- Log the lifecycle (Close callers) to find who closed the session early
Example fix
// before
err := session.Send(ctx, msg)
// after
if !session.Alive() {
session, err = newQoderSession(...)
}
err = session.Send(ctx, msg) Defensive patterns
Strategy: validation
Validate before calling
if !qs.Alive() {
qs, err = newQoderSession(...) // recreate before sending
} Try / catch
if err := session.Send(ctx, msg); err != nil {
if err.Error() == "session is closed" {
session = recreateSession()
return session.Send(ctx, msg)
}
return err
} Prevention
- Check Alive() before every Send
- Never reuse a session after Close(); create a new one
- Avoid sending from callbacks that can fire after teardown
- Track session lifecycle transitions with logs
When it happens
Trigger: Calling Send after Close() has been called on the session; sending on a session whose Close was triggered by teardown/cancellation; engine routing a queued message to an already-closed session.
Common situations: Message queued by the platform while the user ran /new or the session was stopped; a long-running turn completed and the session was closed before a follow-up message arrived; double-close then reuse.
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
- antigravity: unknown permission request %q
- codex app-server connection is closed
- session is closed
- iflowSession: resolve session dir: %w
- session not found: %s
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/8a8fca2913c6e210.
Report an issue: GitHub.