{"record":{"id":"84e5ea5a29e1465a","repo":"chenhg5/cc-connect","slug":"tmux-session-closed","errorCode":null,"errorMessage":"tmux: session closed","messagePattern":"tmux: session closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/tmux/session.go","lineNumber":77,"sourceCode":"\ts := &tmuxSession{\n\t\ttarget:          target,\n\t\tsessionID:       sessionID,\n\t\tworkDir:         workDir,\n\t\tpromptPat:       pat,\n\t\tpollInt:         pollInt,\n\t\tstripInputBlock: stripInputBlock,\n\t\tstripPatterns:   stripPats,\n\t\tevents:          make(chan core.Event, 128),\n\t\tctx:             sessCtx,\n\t\tcancel:          cancel,\n\t}\n\ts.alive.Store(true)\n\treturn s, nil\n}\n\nfunc (s *tmuxSession) Send(prompt string, messageID string, _ []core.ImageAttachment, files []core.FileAttachment) error {\n\tif !s.alive.Load() {\n\t\treturn fmt.Errorf(\"tmux: session closed\")\n\t}\n\n\t// Save attached files and append their paths to the prompt\n\tif len(files) > 0 {\n\t\tpaths := core.SaveFilesToDisk(s.workDir, messageID, files)\n\t\tif len(paths) > 0 {\n\t\t\tprompt = prompt + \"\\n# files: \" + strings.Join(paths, \", \")\n\t\t}\n\t}\n\n\t// Cancel any running poll from a previous Send\n\ts.mu.Lock()\n\tif s.pollCancel != nil {\n\t\ts.pollCancel()\n\t\ts.pollCancel = nil\n\t}\n\n\t// Snapshot the full scrollback (history + visible pane) before sending.","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/tmux/session.go#L59-L95","documentation":"tmuxSession.Send first checks the atomic alive flag; if the session has been closed (or was never fully started), it returns 'tmux: session closed' without touching tmux. The tmux agent models a persistent terminal, so a closed session is terminal — messages cannot be delivered to it.","triggerScenarios":"Calling Send on a tmuxSession after Stop() was called, after the underlying context was canceled (agent shutdown, engine teardown, /new or /switch closing the session), or on a session whose creation path failed to set alive.","commonSituations":"Engine still routing a queued user message to a session the user just closed; a race between the platform handler and session teardown; reusing a stored session reference after a reconnect created a new session object.","solutions":["Verify session lifecycle: only send to the session returned by the current StartSession/lookup, not a stale reference","Handle the error by prompting the user to start a new session (or auto-start one) instead of retrying Send on the closed object","If the race is between teardown and in-flight messages, synchronize with the engine so messages are rejected/redirected before Stop() completes"],"exampleFix":"// caller side\nif err := sess.Send(prompt, msgID, nil, files); err != nil {\n    if strings.Contains(err.Error(), \"session closed\") {\n        sess, err = agent.StartSession(ctx, opts) // recreate\n    }\n}","handlingStrategy":"type-guard","validationCode":"// before sending, ensure the session is current\nif sess == nil || sess.ID() != engine.CurrentSessionID(chatID) {\n    return fmt.Errorf(\"stale session reference\")\n}","typeGuard":"func isSessionClosed(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"tmux: session closed\")\n}","tryCatchPattern":"// Go\nif err := sess.Send(prompt, id, nil, files); isSessionClosed(err) {\n    newSess, serr := agent.StartSession(ctx, opts)\n    if serr == nil {\n        err = newSess.Send(prompt, id, nil, files)\n    }\n}","preventionTips":["Fetch the session via the engine per message instead of caching references","Stop accepting input for a chat as soon as its session is closed","Add an alive/liveness query to session wrappers rather than probing via Send"],"tags":["lifecycle","tmux","closed-session"],"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"}