{"record":{"id":"101946156420ad6f","repo":"can1357/oh-my-pi","slug":"snippet-formatted-lsp-edits-are-unsupported","errorCode":null,"errorMessage":"snippet-formatted LSP edits are unsupported","messagePattern":"snippet-formatted LSP edits are unsupported","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/lsp/edits.ts","lineNumber":87,"sourceCode":"}\n\n/** True when two ranges overlap (share any position other than a touching boundary). */\nexport function rangesOverlap(a: Range, b: Range): boolean {\n\treturn comparePosition(a.start, b.end) < 0 && comparePosition(b.start, a.end) < 0;\n}\n\n/**\n * Sort edits bottom-to-top for in-place application and reject overlaps.\n * Equal start positions tiebreak by original array index descending so that,\n * applied bottom-up, inserts at the same position land in array order\n * (LSP spec: the order of edits in the array defines the order in the result).\n * Byte-identical non-empty range edits are idempotent, so duplicate server\n * output is collapsed before overlap validation.\n */\nfunction rejectSnippetTextEdits(edits: TextEdit[]): void {\n\tfor (const edit of edits) {\n\t\tif (\"insertTextFormat\" in edit && edit.insertTextFormat === 2) {\n\t\t\tthrow new ToolError(\"snippet-formatted LSP edits are unsupported\");\n\t\t}\n\t}\n}\n\nexport function sortAndValidateTextEdits(edits: TextEdit[]): TextEdit[] {\n\trejectSnippetTextEdits(edits);\n\tconst sorted = edits\n\t\t.map((edit, index) => ({ edit, index }))\n\t\t.sort((a, b) => {\n\t\t\tif (a.edit.range.start.line !== b.edit.range.start.line) {\n\t\t\t\treturn b.edit.range.start.line - a.edit.range.start.line;\n\t\t\t}\n\t\t\tif (a.edit.range.start.character !== b.edit.range.start.character) {\n\t\t\t\treturn b.edit.range.start.character - a.edit.range.start.character;\n\t\t\t}\n\t\t\treturn b.index - a.index;\n\t\t})\n\t\t.map(entry => entry.edit);","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/lsp/edits.ts#L69-L105","documentation":"LSP TextEdits may declare insertTextFormat=2, meaning newText is a snippet with placeholders/tab stops (${1:foo}). Snippet syntax is not plain text and cannot be applied literally to the file, so the edit pipeline rejects snippet-formatted edits up front rather than writing snippet markup into source.","triggerScenarios":"A language server returns text edits (e.g. from code actions, rename, or prepare) with insertTextFormat: 2 in the TextEdit payload; sortAndValidateTextEdits runs rejectSnippetTextEdits before applying.","commonSituations":"Servers that emit snippet edits for completion-like code actions; applying quickfixes from servers configured with snippet support; version changes where a server started emitting snippets.","solutions":["Use a code action / edit kind that produces plain-text edits, or configure the server to disable snippet support","Convert snippet newText to plain text (strip ${...} placeholders) before passing to the edit pipeline","Report/patch the server integration to not request snippet-formatted edits (e.g. don't advertise snippetCapability)"],"exampleFix":"// before: passing server edit through verbatim\napplyEdits(serverResponse.edit.entries);\n// after: strip snippet formatting first\nconst plain = edits.map(e => ({...e, insertTextFormat: 1, newText: stripSnippetTokens(e.newText)}));\napplyEdits(plain);","handlingStrategy":"validation","validationCode":"function hasSnippetEdits(edits) {\n  return edits.some(e => 'insertTextFormat' in e && e.insertTextFormat === 2);\n}\nif (hasSnippetEdits(serverEdits)) {\n  // convert or reject before calling sortAndValidateTextEdits\n}","typeGuard":"function isPlainTextEdit(e) {\n  return !('insertTextFormat' in e) || e.insertTextFormat !== 2;\n}","tryCatchPattern":null,"preventionTips":["Configure servers to not emit snippets (don't advertise snippet capability in client capabilities)","Sanitize newText by stripping ${...} snippet tokens before applying edits","Filter insertTextFormat===2 edits out of server responses early in the pipeline"],"tags":["lsp","edits","snippets","unsupported-feature"],"backgroundTag":"snippet-formatted-lsp-edit-unsupported","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}