{"record":{"id":"95f1d399ed7f526f","repo":"chenhg5/cc-connect","slug":"session-is-closed-95f1d3","errorCode":null,"errorMessage":"session is closed","messagePattern":"session is closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/iflow/session.go","lineNumber":130,"sourceCode":"\n\tif resumeID != \"\" && resumeID != core.ContinueSession {\n\t\ts.sessionID.Store(resumeID)\n\t\ts.sentOnce.Store(true)\n\t}\n\n\treturn s, nil\n}\n\nfunc (s *iflowSession) Send(prompt string, messageID string, images []core.ImageAttachment, files []core.FileAttachment) error {\n\tif len(images) > 0 {\n\t\tslog.Warn(\"iflowSession: images are not supported, ignoring\")\n\t}\n\tif len(files) > 0 {\n\t\tfilePaths := core.SaveFilesToDisk(s.workDir, messageID, files)\n\t\tprompt = core.AppendFileRefs(prompt, filePaths)\n\t}\n\tif !s.alive.Load() {\n\t\treturn fmt.Errorf(\"session is closed\")\n\t}\n\tif !s.turnActive.CompareAndSwap(false, true) {\n\t\treturn fmt.Errorf(\"iflow session is busy\")\n\t}\n\n\tturnCtx, turnCancel := context.WithCancel(s.ctx)\n\tturn := &iflowTurn{\n\t\tcancel:         turnCancel,\n\t\tstartedAt:      time.Now(),\n\t\tmode:           s.mode,\n\t\tpendingTimeout: s.pendingToolTimeout(),\n\t\tprocessDone:    make(chan struct{}),\n\t\tpendingToolIDs: make(map[string]struct{}),\n\t\tpendingTools:   make(map[string]iflowToolUse),\n\t\tseenToolIDs:    make(map[string]struct{}),\n\t\tdoneToolIDs:    make(map[string]struct{}),\n\t}\n","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/iflow/session.go#L112-L148","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nerr := session.Send(ctx, prompt, nil)\n// after\nif !session.IsAlive() {\n    session, err = agent.StartSession(ctx, opts)\n}\nif err := session.Send(ctx, prompt, nil); err != nil {\n    if strings.Contains(err.Error(), \"session is closed\") {\n        session, err = agent.StartSession(ctx, opts)\n        err = session.Send(ctx, prompt, nil)\n    }\n}","handlingStrategy":"try-catch","validationCode":"if session == nil || !session.IsAlive() {\n    session, err = agent.StartSession(ctx, opts)\n}","typeGuard":"func sessionUsable(s core.AgentSession, alive func() bool) bool { return s != nil && alive() }","tryCatchPattern":"if err := session.Send(ctx, msg, nil); err != nil {\n    if strings.Contains(err.Error(), \"session is closed\") {\n        session, err = agent.StartSession(ctx, opts)\n        if err == nil { err = session.Send(ctx, msg, nil) }\n    }\n}","preventionTips":["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"],"tags":["go","session-lifecycle","agent","iflow"],"backgroundTag":"invalid-state-transition","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}