{"record":{"id":"18373c13c4aa2845","repo":"gitbutlerapp/gitbutler","slug":"when-using-lm-studio-you-must-provide-a-valid-end","errorCode":null,"errorMessage":"When using LM Studio, you must provide a valid endpoint","messagePattern":"When using LM Studio, you must provide a valid endpoint","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/src/lib/ai/service.ts","lineNumber":331,"sourceCode":"\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}\n\n\t\t\treturn new LMStudioClient(lmStudioEndpoint, lmStudioModelName);\n\t\t}\n\n\t\tif (modelKind === ModelKind.OpenAI) {\n\t\t\tconst openAIModelName = await this.getOpenAIModelName();\n\t\t\tconst openAIKey = await this.getOpenAIKey();\n\t\t\tconst openAICustomEndpoint = await this.getOpenAICustomEndpoint();\n\n\t\t\tif (!openAIKey) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"When using OpenAI in a bring your own key configuration, you must provide a valid token\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn new OpenAIClient(openAIKey, openAIModelName, openAICustomEndpoint);\n\t\t}","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/apps/desktop/src/lib/ai/service.ts#L313-L349","documentation":"Thrown by the hunk-discard path when wt_change.unified_patch_with_filter(...) does not return UnifiedPatch::Patch. The enum also has Binary and TooLarge variants, so this fires when the worktree change is a binary change or the blob exceeds the size limit - there are no text hunks to subtract from, which the hunk algorithm requires.","triggerScenarios":"Calling discard_workspace_changes with hunk_headers for a path whose current or previous content is binary (image, wasm, compiled artifact, or a file detected binary by the gix pipeline), or a file larger than the internal limit; occasionally also a race where the change vanished and the diff yields no patch.","commonSituations":"A file that used to be text replaced by binary content (or vice versa), build artifacts committed accidentally, large datasets/minified bundles exceeding the diff limit, or the file being touched by another process between status and discard.","solutions":["Use whole-file discard for that path: send the DiffSpec with empty hunk_headers","Verify with git diff --binary / file(1) that the content is actually binary or oversized; if it is a misdetected text file, check .gitattributes filters or encoding (e.g. UTF-16 looks binary)","Re-run the discard after refreshing worktree changes to rule out a race with a concurrent writer","For legitimately large text files, whole-file mode is the only supported path"],"exampleFix":"// before\nlet specs = vec![DiffSpec { path, previous_path: None, hunk_headers: selected_hunks }];\n\n// after\nlet specs = vec![DiffSpec { path, previous_path: None, hunk_headers: Vec::new() }];","handlingStrategy":"validation","validationCode":"let wt = but_core::diff::worktree_changes(repo)?;\nfor spec in &changes {\n    if spec.hunk_headers.is_empty() { continue; }\n    if let Some(change) = wt.changes.iter().find(|c| c.path == spec.path) {\n        match change.unified_patch(repo, 3)? {\n            but_core::UnifiedPatch::Patch { .. } => {}\n            _ => panic!(\"{} is binary/too-large: use whole-file discard\", spec.path),\n        }\n    }\n}","typeGuard":"fn has_text_hunks(patch: &but_core::UnifiedPatch) -> bool {\n    matches!(patch, but_core::UnifiedPatch::Patch { .. })\n}","tryCatchPattern":"if let Err(err) = discard_workspace_changes(repo, specs, ctx) {\n    if err.to_string().contains(\"Couldn't obtain diff\") {\n        // retry offending path with empty hunk_headers (whole-file restore)\n    } else { return Err(err); }\n}","preventionTips":["Check .gitattributes (-diff/binary markings) before offering line-level discard","For large files, default the UI to whole-file discard"],"tags":["rust","git","gitbutler","binary-files","diff","discard"],"backgroundTag":"binary-file-diff-unsupported","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-23T16:17:53.355Z"}