{"record":{"id":"9a97584998173686","repo":"chenhg5/cc-connect","slug":"session-is-closed-9a9758","errorCode":null,"errorMessage":"session is closed","messagePattern":"session is closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"agent/codex/appserver_session.go","lineNumber":446,"sourceCode":"\tdefer s.runtimeMu.RUnlock()\n\treturn cloneContextUsage(s.context)\n}\n\nfunc (s *appServerSession) storeUsage(report *core.UsageReport) {\n\ts.runtimeMu.Lock()\n\tdefer s.runtimeMu.Unlock()\n\ts.usage = cloneUsageReport(report)\n}\n\nfunc (s *appServerSession) storeContextUsage(usage *core.ContextUsage) {\n\ts.runtimeMu.Lock()\n\tdefer s.runtimeMu.Unlock()\n\ts.context = cloneContextUsage(usage)\n}\n\nfunc (s *appServerSession) Send(prompt string, messageID string, images []core.ImageAttachment, files []core.FileAttachment) error {\n\tif !s.alive.Load() {\n\t\treturn fmt.Errorf(\"session is closed\")\n\t}\n\n\tif len(files) > 0 {\n\t\tfilePaths := core.SaveFilesToDisk(s.workDir, messageID, files)\n\t\tprompt = core.AppendFileRefs(prompt, filePaths)\n\t}\n\n\tprompt, imagePaths, err := s.stageImages(prompt, images)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\ts.stateMu.Lock()\n\tif !s.preambleSent {\n\t\tprompt = prependCodexPromptPreamble(prompt, s.promptPreamble)\n\t\ts.preambleSent = true\n\t}\n\ts.stateMu.Unlock()","sourceCodeStart":428,"sourceCodeEnd":464,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/codex/appserver_session.go#L428-L464","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nerr := session.Send(prompt, messageID, nil, nil)\nif err != nil {\n    return err\n}\n// after\nerr := session.Send(prompt, messageID, nil, nil)\nif err != nil {\n    if strings.Contains(err.Error(), \"session is closed\") {\n        session, err = agent.StartSession(ctx, resumeOpts) // recreate\n        if err == nil {\n            err = session.Send(prompt, messageID, nil, nil)\n        }\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"// Track session liveness in the caller before sending\nif closed.Load() {\n    return errors.New(\"session already closed; recreate before sending\")\n}","typeGuard":null,"tryCatchPattern":"if err := session.Send(ctx, prompt); err != nil {\n    if strings.Contains(err.Error(), \"session is closed\") {\n        // recreate and retry once, or notify the user the session ended\n        newSession, serr := agent.StartSession(ctx, opts)\n        if serr == nil {\n            return newSession.Send(ctx, prompt)\n        }\n    }\n    return err\n}","preventionTips":["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."],"tags":["session-lifecycle","state-error","codex","race-condition"],"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-14T05:17:10.506Z"}