{"record":{"id":"039c214af425a100","repo":"can1357/oh-my-pi","slug":"overlapping-lsp-edits-formatrange-earlier-con","errorCode":null,"errorMessage":"overlapping LSP edits: ${formatRange(earlier)} conflicts with ${formatRange(later)}; LSP produced inconsistent edits","messagePattern":"overlapping LSP edits: (.+?) conflicts with (.+?); LSP produced inconsistent edits","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/lsp/edits.ts","lineNumber":122,"sourceCode":"\t\t})\n\t\t.map(entry => entry.edit);\n\tconst unique: TextEdit[] = [];\n\tfor (const edit of sorted) {\n\t\tconst prev = unique[unique.length - 1];\n\t\tif (prev && !isEmptyRange(edit.range) && rangesEqual(prev.range, edit.range) && prev.newText === edit.newText) {\n\t\t\tcontinue;\n\t\t}\n\t\tunique.push(edit);\n\t}\n\n\t// Detect overlapping ranges: in reverse-sorted order, each edit's start\n\t// must be >= the next edit's end. If not, the edits would clobber each other\n\t// once applied bottom-up.\n\tfor (let i = 0; i < unique.length - 1; i++) {\n\t\tconst later = unique[i].range;\n\t\tconst earlier = unique[i + 1].range;\n\t\tif (comparePosition(earlier.end, later.start) > 0) {\n\t\t\tthrow new ToolError(\n\t\t\t\t`overlapping LSP edits: ${formatRange(earlier)} conflicts with ${formatRange(later)}; LSP produced inconsistent edits`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn unique;\n}\n\n/**\n * Flatten a WorkspaceEdit's text edits into a Map<uri, TextEdit[]>.\n * Resource operations (create/rename/delete) are ignored — callers handle them separately.\n */\nexport function flattenWorkspaceTextEdits(edit: WorkspaceEdit): Map<string, TextEdit[]> {\n\tconst out = new Map<string, TextEdit[]>();\n\tconst push = (uri: string, edits: TextEdit[]) => {\n\t\tif (edits.length === 0) return;\n\t\tconst prev = out.get(uri);\n\t\tif (prev) prev.push(...edits);","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/lsp/edits.ts#L104-L140","documentation":"After sorting edits bottom-up and collapsing byte-identical duplicates, the validator walks adjacent pairs: each earlier (later-in-document) edit's range end must not extend past the next edit's start. If ranges overlap, applying bottom-up would clobber offsets and corrupt the file, so a ToolError names the two conflicting ranges.","triggerScenarios":"A server returns two text edits whose ranges overlap after dedup (e.g. two code actions both covering the same lines, or a malformed computeEdit output); sortAndValidateTextEdits detects comparePosition(earlier.end, later.start) > 0.","commonSituations":"Server bugs producing inconsistent edits; combining edits from multiple code actions on overlapping regions; renamed/refactored server emitting stale ranges.","solutions":["Request only one code action at a time instead of merging multiple actions' edits","Fix or update the language server producing overlapping ranges","Split application: apply each edit set to a fresh document state sequentially"],"exampleFix":"// before: merging edits from two actions\nsortAndValidateTextEdits([...actionAEdits, ...actionBEdits]);\n// after: apply sequentially\napplyEdits(actionAEdits); applyEdits(refreshEdits(actionBEdits));","handlingStrategy":"validation","validationCode":"function rangesOverlap(a, b) {\n  const cmp = (p, q) => p.line - q.line || p.character - q.character;\n  return !(cmp(a.end, b.start) <= 0 || cmp(b.end, a.start) <= 0);\n}\nfor (let i = 0; i < edits.length; i++)\n  for (let j = i + 1; j < edits.length; j++)\n    if (rangesOverlap(edits[i].range, edits[j].range)) throw new Error('server produced overlapping edits');","typeGuard":null,"tryCatchPattern":"try {\n  const sorted = sortAndValidateTextEdits(edits);\n} catch (err) {\n  if (err.message.startsWith('overlapping LSP edits:')) {\n    // fall back to applying edits one action at a time, re-querying between batches\n  }\n  throw err;\n}","preventionTips":["Apply one code action's edits at a time rather than merging multiple actions","Keep language servers updated — overlapping edits usually indicate a server bug","Deduplicate identical edits before validation"],"tags":["lsp","edits","overlap","server-bug"],"backgroundTag":"overlapping-lsp-edits","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}