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 byteOffset

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Update the LSP protocol/fantasy library so AsTextEdit supports the edit variants your server emits
  2. Inspect edit.Kind/annotations in the failing element and handle/strip it before calling ApplyWorkspaceEdit
  3. 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

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


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/470ddb7c7b83bff2. Report an issue: GitHub.