{"record":{"id":"a29148e3d92661a0","repo":"gitbutlerapp/gitbutler","slug":"invalid-response-json-stringify-result","errorCode":null,"errorMessage":"Invalid response: ${JSON.stringify(result)}","messagePattern":"Invalid response: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/src/lib/ai/ollamaClient.ts","lineNumber":84,"sourceCode":"\t * Sends a chat message to the LLM model and returns the response.\n\t *\n\t * @param messages - An array of LLMChatMessage objects representing the chat messages.\n\t * @param options - Optional LLMRequestOptions object for specifying additional options.\n\t * @returns A Promise that resolves to an LLMResponse object representing the response from the LLM model.\n\t */\n\tprivate async chat(\n\t\tmessages: Prompt,\n\t\toptions?: OllamaRequestOptions,\n\t): Promise<OllamaChatResponse> {\n\t\tconst result = await this.ollama.chat({\n\t\t\tmodel: this.modelName,\n\t\t\tmessages,\n\t\t\tstream: false,\n\t\t\toptions,\n\t\t});\n\n\t\tif (!isOllamaChatResponse(result)) {\n\t\t\tthrow new Error(\"Invalid response: \" + JSON.stringify(result));\n\t\t}\n\n\t\treturn result;\n\t}\n}\n","sourceCodeStart":66,"sourceCodeEnd":90,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/apps/desktop/src/lib/ai/ollamaClient.ts#L66-L90","documentation":"Thrown during hunk-based discard when the matched Modification/Rename change has a typechange flag, meaning the path changed blob kind between the previous state and the worktree (file to symlink, file to submodule/gitlink, etc.). Line hunks only make sense between two text blobs of the same kind, so the library refuses and asks for whole-file mode.","triggerScenarios":"discard_workspace_changes with a spec containing hunk_headers where the worktree change is TreeStatus::Modification or Rename whose flags satisfy f.is_typechange() - e.g. the user replaced a regular file with a symlink, or a directory became a submodule, then tried to discard selected lines of it.","commonSituations":"Editor/tooling replacing files with symlinks (dependency dirs, nix-style linking), git submodules added where a file used to be, or a stale diff computed before the type change. Windows dev drives or core.symlinks toggles can also surface file<->symlink typechanges.","solutions":["Discard the whole file instead: send the DiffSpec with empty hunk_headers, which restores the previous state including its object kind","Recompute worktree changes so the typechange is visible in the UI and line-level discard is not offered","If the typechange is unintended (e.g. accidental symlink), resolve it in the worktree first, then redo the hunk discard"],"exampleFix":"// before\nlet specs = vec![DiffSpec { path, previous_path: None, hunk_headers: selected_hunks }];\n\n// after: whole-file restore handles the kind change\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        if let but_core::TreeStatus::Modification { flags: Some(f), .. } = &change.status {\n            assert!(!f.is_typechange(), \"{} is type-changed: use whole-file mode\", spec.path);\n        }\n    }\n}","typeGuard":"fn is_typechange(status: &but_core::TreeStatus) -> bool {\n    matches!(status,\n        but_core::TreeStatus::Modification { flags: Some(f), .. }\n        | but_core::TreeStatus::Rename { flags: Some(f), .. } if f.is_typechange())\n}","tryCatchPattern":"if let Err(err) = discard_workspace_changes(repo, specs, ctx) {\n    if err.to_string().contains(\"Type-changed\") {\n        // fall back: same spec with hunk_headers cleared\n    } else { return Err(err); }\n}","preventionTips":["Surface typechange status in the UI and offer only whole-file discard for it","Re-check worktree state after symlink-affecting operations (core.symlinks changes, linkers)"],"tags":["rust","git","gitbutler","typechange","symlink","discard"],"backgroundTag":"unsupported-operation-mode","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-23T16:17:53.355Z"}