{"record":{"id":"679986215262d474","repo":"dagger/dagger","slug":"no-llm-session-active","errorCode":null,"errorMessage":"no LLM session active","messagePattern":"no LLM session active","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cmd/dagger/llm.go","lineNumber":485,"sourceCode":"\t\t\tvar buf strings.Builder\n\t\t\tpatchpreview.Summarize(idtui.NewOutput(&buf), entries, width)\n\t\t\treturn buf.String()\n\t\t},\n\t\tKeyMap: []key.Binding{\n\t\t\tkey.NewBinding(key.WithKeys(\"ctrl+s\"), key.WithHelp(\"ctrl+s\", \"save\")),\n\t\t\tkey.NewBinding(key.WithKeys(\"ctrl+u\"), key.WithHelp(\"ctrl+u\", \"reset\")),\n\t\t},\n\t})\n\treturn nil\n}\n\n// ExportChanges writes the workspace's pending overlay edits to its local Git\n// workspace (Workspace.export), then refreshes the changes preview. It is the\n// ctrl+s action; export fails clearly when the workspace cannot persist (a\n// remote ref, a synthetic workspace, or a local dir with no Git root).\nfunc (s *LLMSession) ExportChanges(ctx context.Context) error {\n\tif s.llm == nil {\n\t\treturn fmt.Errorf(\"no LLM session active\")\n\t}\n\tif err := s.llm.Workspace().Export(ctx); err != nil {\n\t\treturn err\n\t}\n\t// The exported edits now live on disk, so rebind the live workspace: the\n\t// overlay the agent accumulated is now redundant with the files\n\t// themselves, and carrying it forward would re-diff already-saved content\n\t// as pending changes. Rebinding also drops it from the next save —\n\t// portableID emits only the current binding. Export bumps the client's\n\t// workspace read epoch, so reads after this point see the saved content\n\t// rather than a snapshot cached earlier in the session. Sync eagerly so a\n\t// failure surfaces here rather than corrupting later saves.\n\trebound, err := s.llm.WithWorkspace(s.dag.CurrentWorkspace()).Sync(ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"rebind workspace after export: %w\", err)\n\t}\n\tif err := s.updateLLM(rebound); err != nil {\n\t\treturn err","sourceCodeStart":467,"sourceCodeEnd":503,"githubUrl":"https://github.com/dagger/dagger/blob/82ba2681dbe30d3547a1dc50ea495900ab5b6047/internal/cmd/dagger/llm.go#L467-L503","documentation":"ExportChanges performs the ctrl+s action: exporting the workspace's pending overlay edits to the local Git workspace. It requires an active LLM session; when s.llm is nil there is nothing to export and the error is returned.","triggerScenarios":"Calling LLMSession.ExportChanges before any prompt/session was started (s.llm == nil), e.g. via a keybinding or API call on a freshly constructed session.","commonSituations":"Calling ExportChanges in a script or TUI binding before the first prompt; resetting a session then attempting to export; UI state desync after session teardown.","solutions":["Start the session (send a prompt via WithPrompt) before calling ExportChanges","Guard the call: skip export when no session is active","Re-create or reinitialize the session if it was reset unexpectedly"],"exampleFix":"// before\nsession.ExportChanges(ctx) // panics-free but errors: no LLM session active\n// after\nif session.LLM() != nil {\n    session.ExportChanges(ctx)\n}","handlingStrategy":"type-guard","validationCode":"if session == nil || !session.Active() { return errors.New(\"start a prompt before exporting changes\") }","typeGuard":"func (s *LLMSession) HasLLM() bool { return s != nil && s.llm != nil }","tryCatchPattern":"if err := session.ExportChanges(ctx); err != nil && err.Error() == \"no LLM session active\" { /* start session or skip */ }","preventionTips":["Track session lifecycle state in UI bindings","Disable ctrl+s/export affordances until the first prompt","Reset-dependent code should reinitialize before exporting"],"tags":["llm","workspace","session-state"],"backgroundTag":"no-active-session","analyzedSha":"82ba2681dbe30d3547a1dc50ea495900ab5b6047","analyzedAt":"2026-09-05T07:21:37.930Z","contentChangedAt":"2026-09-05T07:21:37.930Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}