{"record":{"id":"a6b748528786dbb3","repo":"chenhg5/cc-connect","slug":"session-is-closed","errorCode":null,"errorMessage":"session is closed","messagePattern":"session is closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/antigravity/session.go","lineNumber":77,"sourceCode":"\tif mode == \"default\" {\n\t\tbridge, err := newAgyPermissionBridge(sessionCtx, as.events)\n\t\tif err != nil {\n\t\t\tcancel()\n\t\t\treturn nil, fmt.Errorf(\"antigravity: initialize permission bridge: %w\", err)\n\t\t}\n\t\tas.permissionBridge = bridge\n\t}\n\n\tif resumeID != \"\" && resumeID != core.ContinueSession {\n\t\tas.chatID.Store(resumeID)\n\t}\n\n\treturn as, nil\n}\n\nfunc (as *antigravitySession) Send(prompt string, messageID string, images []core.ImageAttachment, files []core.FileAttachment) error {\n\tif !as.alive.Load() {\n\t\treturn fmt.Errorf(\"session is closed\")\n\t}\n\n\t// Capture existing chat logs so we can identify a new session on first turn\n\tpreEntries := make(map[string]bool)\n\thomeDir, err := os.UserHomeDir()\n\tif err == nil {\n\t\tslug := antigravityProjectSlug(as.workDir)\n\t\tchatsDir := filepath.Join(homeDir, \".gemini\", \"tmp\", slug, \"chats\")\n\t\tif entries, err := os.ReadDir(chatsDir); err == nil {\n\t\t\tfor _, entry := range entries {\n\t\t\t\tif !entry.IsDir() && strings.HasSuffix(entry.Name(), \".jsonl\") {\n\t\t\t\t\tpreEntries[entry.Name()] = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Save images and files into the workspace","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/agent/antigravity/session.go#L59-L95","documentation":"antigravitySession.Send checks the atomic 'alive' flag and refuses to submit a prompt once the session has been closed or the underlying process has exited. It is a lifecycle guard against using a dead session handle.","triggerScenarios":"Calling Send on a session after Stop()/Close(), after the agy process exited, or after an event-stream terminal event marked the session dead.","commonSituations":"The engine's session cache retaining an expired session; agy crashing mid-session and the caller retrying Send on the same handle; messages arriving from the platform after the user ran /stop.","solutions":["Start a fresh session via StartSession instead of reusing the closed one.","Check session liveness (if exposed) before Send, or recreate the session on this error.","In platform handlers, catch this error and fall back to creating a new session for the user's next message.","Investigate why the session died earlier (check the error event stream / agy stderr)."],"exampleFix":"// before\nerr := cachedSession.Send(prompt, id, nil, nil)\n// after\nif err := cachedSession.Send(prompt, id, nil, nil); err != nil && err.Error() == \"session is closed\" {\n    cachedSession, err = agent.StartSession(ctx, opts)\n    err = cachedSession.Send(prompt, id, nil, nil)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := session.Send(prompt, id, nil, nil); err != nil {\n    if err.Error() == \"session is closed\" {\n        session, err = agent.StartSession(ctx, opts)\n        if err == nil { err = session.Send(prompt, id, nil, nil) }\n    }\n}","preventionTips":["Evict dead sessions from your session cache on terminal events.","Recreate the session automatically when agy exits unexpectedly.","Stop routing platform messages to a session after /stop."],"tags":["go","session-lifecycle","antigravity"],"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"}