chenhg5/cc-connect · warning
session is closed
Error message
session is closed
What it means
This error is returned by appServerSession.Send when the session's alive flag is false, i.e. Close() has already been called or the session terminated. Sending on a closed session is rejected up front so writes never target dead pipes. It is a state error, not a transport error — the caller attempted to use a session past its lifetime.
Source
Thrown at agent/codex/appserver_session.go:446
defer s.runtimeMu.RUnlock()
return cloneContextUsage(s.context)
}
func (s *appServerSession) storeUsage(report *core.UsageReport) {
s.runtimeMu.Lock()
defer s.runtimeMu.Unlock()
s.usage = cloneUsageReport(report)
}
func (s *appServerSession) storeContextUsage(usage *core.ContextUsage) {
s.runtimeMu.Lock()
defer s.runtimeMu.Unlock()
s.context = cloneContextUsage(usage)
}
func (s *appServerSession) Send(prompt string, messageID string, images []core.ImageAttachment, files []core.FileAttachment) error {
if !s.alive.Load() {
return fmt.Errorf("session is closed")
}
if len(files) > 0 {
filePaths := core.SaveFilesToDisk(s.workDir, messageID, files)
prompt = core.AppendFileRefs(prompt, filePaths)
}
prompt, imagePaths, err := s.stageImages(prompt, images)
if err != nil {
return err
}
s.stateMu.Lock()
if !s.preambleSent {
prompt = prependCodexPromptPreamble(prompt, s.promptPreamble)
s.preambleSent = true
}
s.stateMu.Unlock()View on GitHub (pinned to 4000b2338a)
Solutions
- Check session liveness before sending (or rely on this error to trigger re-creating the session via StartSession).
- Serialize session teardown vs. sends with the engine's session lock so in-flight messages are drained before Close.
- If the error is from a race in the engine, treat it as benign: drop the message and notify the user the session ended.
- Re-establish the session: create a new session (with resumeID of the old thread if history should continue) and retry the prompt.
Example fix
// before
err := session.Send(prompt, messageID, nil, nil)
if err != nil {
return err
}
// after
err := session.Send(prompt, messageID, nil, nil)
if err != nil {
if strings.Contains(err.Error(), "session is closed") {
session, err = agent.StartSession(ctx, resumeOpts) // recreate
if err == nil {
err = session.Send(prompt, messageID, nil, nil)
}
}
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
// Track session liveness in the caller before sending
if closed.Load() {
return errors.New("session already closed; recreate before sending")
} Try / catch
if err := session.Send(ctx, prompt); err != nil {
if strings.Contains(err.Error(), "session is closed") {
// recreate and retry once, or notify the user the session ended
newSession, serr := agent.StartSession(ctx, opts)
if serr == nil {
return newSession.Send(ctx, prompt)
}
}
return err
} Prevention
- Drain in-flight sends before calling Close (engine-level session lock).
- Recreate sessions after any 'session is closed' error instead of retrying on the same object.
- Avoid holding session references across long waits; re-resolve from the session manager.
- Enable race-detector tests for concurrent send/close paths.
When it happens
Trigger: Calling Send on an appServerSession after Close() (or Stop) completed; a queued/async message lands on a session another goroutine just closed; engine retries a prompt after the session was torn down due to a previous error.
Common situations: Concurrent message handling where a user cancels/closes the session while a message is in flight; engine not refreshing session references after session expiry; daemon shutdown racing an inbound platform message.
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
- no active session
- acp: session closed
- antigravity: permission request %q is already resolved
- session is closed
- codex app-server resume returned empty thread id
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/9a97584998173686.
Report an issue: GitHub.