charmbracelet/crush · error
invalid edit type: %w
Error message
invalid edit type: %w
What it means
applyDocumentChange converts each TextDocumentEdit entry via edit.AsTextEdit(). If an edit cannot be represented as a protocol.TextEdit (wrong concrete type / malformed kind), the error from AsTextEdit is wrapped as "invalid edit type". It indicates the WorkspaceEdit payload contains an edit shape the client cannot handle.
Source
Thrown at internal/lsp/util/edit.go:241
if change.RenameFile.Options != nil {
if !change.RenameFile.Options.Overwrite {
if _, err := os.Stat(newPath); err == nil {
return fmt.Errorf("target file already exists and overwrite is not allowed: %s", newPath)
}
}
}
if err := os.Rename(oldPath, newPath); err != nil {
return fmt.Errorf("failed to rename file: %w", err)
}
}
if change.TextDocumentEdit != nil {
textEdits := make([]protocol.TextEdit, len(change.TextDocumentEdit.Edits))
for i, edit := range change.TextDocumentEdit.Edits {
var err error
textEdits[i], err = edit.AsTextEdit()
if err != nil {
return fmt.Errorf("invalid edit type: %w", err)
}
}
return applyTextEdits(change.TextDocumentEdit.TextDocument.URI, textEdits, encoding)
}
return nil
}
// utf32ToByteOffset converts a UTF-32 codepoint offset to a byte offset.
func utf32ToByteOffset(lineText string, codepointOffset uint32) int {
if codepointOffset == 0 {
return 0
}
var codepointCount uint32
for byteOffset := range lineText {
if codepointCount >= codepointOffset {
return byteOffsetView on GitHub (pinned to 7944b8e522)
Solutions
- Update the LSP protocol/fantasy library so AsTextEdit supports the edit variants your server emits
- Inspect edit.Kind/annotations in the failing element and handle/strip it before calling ApplyWorkspaceEdit
- Report or work around a server emitting non-conformant edits; disable that feature if necessary
Example fix
// before
textEdits[i], err = edit.AsTextEdit()
// after
textEdits[i], err = edit.AsTextEdit()
if err != nil {
if ann, ok := edit.(protocol.AnnotatedTextEdit); ok {
textEdits[i] = ann.TextEdit // strip annotation
continue
}
return fmt.Errorf("invalid edit type: %w", err)
} Defensive patterns
Strategy: type-guard
Validate before calling
for _, e := range docEdit.Edits {
if _, err := e.AsTextEdit(); err != nil {
return fmt.Errorf("unsupported edit in %s: %w", docEdit.TextDocument.URI, err)
}
} Type guard
func isPlainTextEdit(e protocol.TextEditOrAnnotated) bool {
_, err := e.AsTextEdit()
return err == nil
} Try / catch
if err := ApplyWorkspaceEdit(edit, enc); err != nil && strings.Contains(err.Error(), "invalid edit type") {
// filter out unsupported edits and retry with the remainder
} Prevention
- Keep the LSP protocol library (fantasy/powernap) up to date to support annotated edits
- Prefer servers known to emit plain TextEdits, or strip annotations client-side
- Log and inspect rejected edit elements when integrating a new server
When it happens
Trigger: A TextDocumentEdit.Edits element is a protocol.AnnotatedTextEdit or another variant whose AsTextEdit fails, typically because Kind/annotation data is malformed or the element is nil of an unexpected type.
Common situations: Newer LSP servers emitting AnnotatedTextEdit entries; protocol/version mismatches between server and client; buggy server emitting non-conformant edit JSON.
Related errors
- failed to initialize the lsp client: %w
- invalid URI: %w
- failed to read file: %w
- overlapping edits detected between edit %d and %d
- failed to apply edit: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/470ddb7c7b83bff2.
Report an issue: GitHub.