gitbutlerapp/gitbutler · error

Failed to communicate with LM Studio server: ${error instanc

Error message

Failed to communicate with LM Studio server: ${error instanceof Error ? error.message : String(error)}

What it means

Thrown by discard_workspace_changes when a DiffSpec carries hunk_headers but the matched worktree change is an Addition or Deletion. Hunks describe line-level edits relative to a previous file state; an added file has no previous state and a deleted file has no current content, so 'discard these lines' is undefined. The library demands the whole-file mode, i.e. the same path sent with an empty hunk_headers list.

Source

Thrown at apps/desktop/src/lib/ai/lmStudioClient.ts:119

									result += token;
								}
							}
						} catch (e) {
							console.warn("Error parsing streaming JSON", e);
						}
					}
				}

				return result;
			}
			// Handle non-streaming response
			else {
				const json = await response.json();
				return json.choices[0]?.message?.content || "";
			}
		} catch (error) {
			console.error("Error calling LM Studio API:", error);
			throw new Error(
				`Failed to communicate with LM Studio server: ${error instanceof Error ? error.message : String(error)}`,
			);
		}
	}
}

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Send the spec for that path with an empty hunk_headers Vec (whole-file mode), which restores the previous state (Deletion) or removes/purges the file (Addition)
  2. Re-fetch worktree changes (but_core::diff::worktree_changes) right before discarding and rebuild the DiffSpecs from the fresh statuses
  3. Filter your change list so only Modification/Rename statuses carry hunk_headers before calling the API
  4. If building a UI, disable line-level discard for added/deleted files and fall back to whole-file discard

Example fix

// before
let specs = vec![DiffSpec { path, previous_path: None, hunk_headers: selected_hunks }];
discard_workspace_changes(repo, specs, 3)?;

// after
let is_add_or_delete = matches!(status, TreeStatus::Addition { .. } | TreeStatus::Deletion { .. });
let specs = vec![DiffSpec {
    path,
    previous_path: None,
    hunk_headers: if is_add_or_delete { Vec::new() } else { selected_hunks },
}];
discard_workspace_changes(repo, specs, 3)?;
Defensive patterns

Strategy: validation

Validate before calling

// before calling discard_workspace_changes
let wt = but_core::diff::worktree_changes(repo)?;
for spec in &changes {
    if spec.hunk_headers.is_empty() { continue; }
    if let Some(change) = wt.changes.iter().find(|c| c.path == spec.path) {
        assert!(!matches!(change.status, but_core::TreeStatus::Addition { .. } | but_core::TreeStatus::Deletion { .. }),
            "path {} is added/deleted: use whole-file mode", spec.path);
    }
}

Type guard

fn supports_hunk_discard(status: &but_core::TreeStatus) -> bool {
    matches!(status, but_core::TreeStatus::Modification { .. } | but_core::TreeStatus::Rename { .. })
}

Try / catch

match discard_workspace_changes(repo, specs, ctx) {
    Ok(dropped) => { /* handle unmatched specs */ }
    Err(err) if err.to_string().contains("whole-file mode") => {
        // retry the offending path with empty hunk_headers
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: Calling discard_workspace_changes(repo, changes, context_lines) where changes contains a DiffSpec with non-empty hunk_headers whose path matches a worktree change whose TreeStatus is Addition (tracked or untracked new file) or Deletion. Typically the UI offers 'discard selected lines' for a file that is new or deleted instead of modified.

Common situations: A diff/selection payload computed when the file was a Modification is replayed after the file was deleted and re-added, or after external git commands (git rm, git checkout, another tool) changed the file's status; stale worktree state in a long-running app; frontends that enable hunk-level discard buttons for all file kinds.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/d7a335d6ed058243. Report an issue: GitHub.