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
- 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)
- Re-fetch worktree changes (but_core::diff::worktree_changes) right before discarding and rebuild the DiffSpecs from the fresh statuses
- Filter your change list so only Modification/Rename statuses carry hunk_headers before calling the API
- 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
- Always rebuild DiffSpecs from a fresh worktree_changes() call immediately before discarding
- Only attach hunk_headers to Modification/Rename changes
- In UIs, gate the 'discard lines' action on the file being modified, not added/deleted
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
- When using LM Studio, you must provide a valid endpoint
- Invalid response: ${JSON.stringify(result)}
- When using OpenAI in a bring your own key configuration, you
- When using GitButler's API to summarize code, you must be lo
- When using Anthropic in a bring your own key configuration,
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/d7a335d6ed058243.
Report an issue: GitHub.