{"record":{"id":"e8e382c049ac1b8a","repo":"gitbutlerapp/gitbutler","slug":"when-using-gitbutler-s-api-to-summarize-code-you","errorCode":null,"errorMessage":"When using GitButler's API to summarize code, you must be logged in","messagePattern":"When using GitButler's API to summarize code, you must be logged in","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/src/lib/ai/service.ts","lineNumber":314,"sourceCode":"\n\tasync validateGitButlerAPIConfiguration(): Promise<boolean> {\n\t\tif (!(await this.usingGitButlerAPI())) {\n\t\t\treturn false;\n\t\t}\n\t\treturn !!get(this.tokenMemoryService.token);\n\t}\n\n\t// This optionally returns a summarizer. There are a few conditions for how this may occur\n\t// Firstly, if the user has opted to use the GB API and isn't logged in, it will return undefined\n\t// Secondly, if the user has opted to bring their own key but hasn't provided one, it will return undefined\n\tasync buildClient(): Promise<AIClient | undefined> {\n\t\tconst modelKind = await this.getModelKind();\n\n\t\tif (await this.usingGitButlerAPI()) {\n\t\t\t// TODO(CTO): Once @estib has landed the new auth, it would be good to\n\t\t\t// about a good way of checking whether the user is authenticated.\n\t\t\tif (!get(this.tokenMemoryService.token)) {\n\t\t\t\tthrow new Error(\"When using GitButler's API to summarize code, you must be logged in\");\n\t\t\t}\n\n\t\t\treturn new ButlerAIClient(this.cloud, modelKind);\n\t\t}\n\n\t\tif (modelKind === ModelKind.Ollama) {\n\t\t\tconst ollamaEndpoint = await this.getOllamaEndpoint();\n\t\t\tconst ollamaModelName = await this.getOllamaModelName();\n\t\t\treturn new OllamaClient(ollamaEndpoint, ollamaModelName);\n\t\t}\n\n\t\tif (modelKind === ModelKind.LMStudio) {\n\t\t\tconst lmStudioEndpoint = await this.getLMStudioEndpoint();\n\t\t\tconst lmStudioModelName = await this.getLMStudioModelName();\n\n\t\t\tif (!lmStudioEndpoint) {\n\t\t\t\tthrow new Error(\"When using LM Studio, you must provide a valid endpoint\");\n\t\t\t}","sourceCodeStart":296,"sourceCodeEnd":332,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/apps/desktop/src/lib/ai/service.ts#L296-L332","documentation":"Raised when discarding submodule changes: the code shells out to 'git reset --hard <id> && git clean -fxd' inside the submodule directory and the subprocess exits non-zero. The message embeds the submodule path, the target commit id, and git's stderr, so the underlying git failure is the real diagnostic.","triggerScenarios":"discard_workspace_changes on a path whose previous state is a submodule (gitlink) whose worktree content no longer matches state.id. git reset then fails because the commit is missing from the submodule, the submodule checkout is on an unrelated history, files are read-only or locked, the submodule working directory was deleted, or 'git' is not resolvable via the shell.","commonSituations":"Submodule commit not fetched (shallow clone, missing remote fetch), submodule initialized at a different origin, read-only files after a CI copy or Windows permission issues, antivirus/index.lock contention, or a concurrently running git process inside the submodule.","solutions":["Read the embedded stderr in the error message - it names the exact git failure to fix","Fetch inside the submodule so the target commit exists: git -C <sm_dir> fetch --all-tags, then retry the discard","Repair the submodule checkout manually (git -C <sm_dir> status) and resolve lock/permission issues, then retry","If the submodule directory was removed externally, reinitialize it (git submodule update --init) before discarding"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// verify the submodule can reach the target commit before discarding\nlet sm = std::path::Path::new(sm_dir);\nif sm.join(\".git\").exists() {\n    let out = std::process::Command::new(\"git\")\n        .args([\"-C\", sm_dir, \"cat-file\", \"-e\", &format!(\"{id}^{{commit}}\", id = state.id)])\n        .output()?;\n    anyhow::ensure!(out.status.success(), \"submodule cannot resolve {} - fetch it first\", state.id);\n}","typeGuard":null,"tryCatchPattern":"match discard_workspace_changes(repo, specs, ctx) {\n    Err(err) if err.to_string().contains(\"Could not reset submodule\") => {\n        // log err (contains git stderr), fetch in the submodule, retry once\n    }\n    other => other.map(|_| ()),\n}","preventionTips":["Keep submodules fetched (git submodule update --remote or periodic fetch) before discarding their changes","Ensure 'git' is on PATH for processes using gix shell-outs","Avoid running other git commands inside submodule directories mid-operation"],"tags":["rust","git","gitbutler","submodule","subprocess","reset"],"backgroundTag":"git-submodule-reset-failed","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-23T16:17:53.355Z"}