can1357/oh-my-pi · error · ToolError
snippet-formatted LSP edits are unsupported
Error message
snippet-formatted LSP edits are unsupported
What it means
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.
Source
Thrown at packages/coding-agent/src/lsp/edits.ts:87
}
/** True when two ranges overlap (share any position other than a touching boundary). */
export function rangesOverlap(a: Range, b: Range): boolean {
return comparePosition(a.start, b.end) < 0 && comparePosition(b.start, a.end) < 0;
}
/**
* Sort edits bottom-to-top for in-place application and reject overlaps.
* Equal start positions tiebreak by original array index descending so that,
* applied bottom-up, inserts at the same position land in array order
* (LSP spec: the order of edits in the array defines the order in the result).
* Byte-identical non-empty range edits are idempotent, so duplicate server
* output is collapsed before overlap validation.
*/
function rejectSnippetTextEdits(edits: TextEdit[]): void {
for (const edit of edits) {
if ("insertTextFormat" in edit && edit.insertTextFormat === 2) {
throw new ToolError("snippet-formatted LSP edits are unsupported");
}
}
}
export function sortAndValidateTextEdits(edits: TextEdit[]): TextEdit[] {
rejectSnippetTextEdits(edits);
const sorted = edits
.map((edit, index) => ({ edit, index }))
.sort((a, b) => {
if (a.edit.range.start.line !== b.edit.range.start.line) {
return b.edit.range.start.line - a.edit.range.start.line;
}
if (a.edit.range.start.character !== b.edit.range.start.character) {
return b.edit.range.start.character - a.edit.range.start.character;
}
return b.index - a.index;
})
.map(entry => entry.edit);View on GitHub (pinned to 9690622007)
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)
Example fix
// before: passing server edit through verbatim
applyEdits(serverResponse.edit.entries);
// after: strip snippet formatting first
const plain = edits.map(e => ({...e, insertTextFormat: 1, newText: stripSnippetTokens(e.newText)}));
applyEdits(plain); Defensive patterns
Strategy: validation
Validate before calling
function hasSnippetEdits(edits) {
return edits.some(e => 'insertTextFormat' in e && e.insertTextFormat === 2);
}
if (hasSnippetEdits(serverEdits)) {
// convert or reject before calling sortAndValidateTextEdits
} Type guard
function isPlainTextEdit(e) {
return !('insertTextFormat' in e) || e.insertTextFormat !== 2;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- overlapping LSP edits: ${formatRange(earlier)} conflicts wit
- --list-details, --exec, and --exec-batch are not supported b
- OpenAI explicit prompt caching is unsupported for ${model.pr
- no built-in uploader or serving adapter is implemented
- SFTP password injection is unsupported by the shared SSH tra
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/101946156420ad6f.
Report an issue: GitHub.