charmbracelet/crush · error

failed to apply document change: %w

Error message

failed to apply document change: %w

What it means

ApplyWorkspaceEdit wraps any error from applyDocumentChange (per-entry in WorkspaceEdit.DocumentChanges) as "failed to apply document change". DocumentChanges is the newer WorkspaceEdit shape that can include both TextDocumentEdit and RenameFile operations. This wrapper preserves the underlying cause via %w.

Source

Thrown at internal/lsp/util/edit.go:280

	}
	return len(lineText)
}

// ApplyWorkspaceEdit applies the given WorkspaceEdit to the filesystem.
// The encoding parameter specifies the position encoding used by the LSP server
// (UTF8, UTF16, or UTF32). This affects how character offsets are interpreted.
func ApplyWorkspaceEdit(edit protocol.WorkspaceEdit, encoding powernap.OffsetEncoding) error {
	// Handle Changes field
	for uri, textEdits := range edit.Changes {
		if err := applyTextEdits(uri, textEdits, encoding); err != nil {
			return fmt.Errorf("failed to apply text edits: %w", err)
		}
	}

	// Handle DocumentChanges field
	for _, change := range edit.DocumentChanges {
		if err := applyDocumentChange(change, encoding); err != nil {
			return fmt.Errorf("failed to apply document change: %w", err)
		}
	}

	return nil
}

// rangesOverlap checks if two LSP ranges overlap.
// Per the LSP specification, ranges are half-open intervals [start, end),
// so adjacent ranges where one's end equals another's start do NOT overlap.
// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#range
func rangesOverlap(r1, r2 protocol.Range) bool {
	if r1.Start.Line > r2.End.Line || r2.Start.Line > r1.End.Line {
		return false
	}
	if r1.Start.Line == r2.End.Line && r1.Start.Character >= r2.End.Character {
		return false
	}
	if r2.Start.Line == r1.End.Line && r2.Start.Character >= r1.End.Character {

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Unwrap with errors.Unwrap / %w chain to find the root cause (rename vs text edit)
  2. Validate all entries first (target paths, document URIs) before applying, to make application closer to atomic
  3. Ensure document versions match; refresh stale documents and re-request the edit

Example fix

// before
if err := ApplyWorkspaceEdit(edit, enc); err != nil {
    return err
}
// after
if err := ApplyWorkspaceEdit(edit, enc); err != nil {
    root := err
    for errors.Unwrap(root) != nil {
        root = errors.Unwrap(root)
    }
    log.Printf("workspace edit failed: %v (cause: %v)", err, root)
    return root
}
Defensive patterns

Strategy: try-catch

Validate before calling

func validateWorkspaceEdit(edit protocol.WorkspaceEdit, openDocs map[string]struct{}) error {
    for _, ch := range edit.DocumentChanges {
        if ch.TextDocumentEdit != nil {
            if _, ok := openDocs[string(ch.TextDocumentEdit.TextDocument.URI)]; !ok {
                return fmt.Errorf("doc not open: %s", ch.TextDocumentEdit.TextDocument.URI)
            }
        }
        if ch.RenameFile != nil {
            if ch.RenameFile.Options == nil || !ch.RenameFile.Options.Overwrite {
                if _, err := os.Stat(string(ch.RenameFile.NewURI)); err == nil {
                    return fmt.Errorf("rename target exists: %s", ch.RenameFile.NewURI)
                }
            }
        }
    }
    return nil
}

Try / catch

if err := ApplyWorkspaceEdit(edit, enc); err != nil {
    root := err
    for errors.Unwrap(root) != nil { root = errors.Unwrap(root) }
    switch {
    case strings.Contains(fmt.Sprint(root), "not exist"):
        // stale document: refresh and retry
    case strings.Contains(err.Error(), "document change"):
        log.Printf("entry failed: %v", err)
    }
}

Prevention

When it happens

Trigger: Any applyDocumentChange failure for a DocumentChanges element: invalid edit type, target-file-exists on rename, os.Rename failure, or applyTextEdits failure inside a TextDocumentEdit.

Common situations: Rename/multi-file refactor operations from the server hitting one bad entry; a rename where the destination exists; encoding mismatch causing position errors; mixed operations where one entry references a stale document version.

Related errors


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