charmbracelet/crush · error

invalid edit type

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.

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.

Appendix: 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)