charmbracelet/crush · error

invalid URI: %w

Error message

invalid URI: %w

What it means

applyTextEdits converts a protocol.DocumentURI to a filesystem path via uri.Path() before reading the file and applying edits. If the URI is not a valid file-scheme URI (no extractable path), the error is wrapped as "invalid URI" and no edits are applied. It is reached from applyDocumentChange and ApplyWorkspaceEdit when handling server-initiated edits.

Source

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

package util

import (
	"bytes"
	"fmt"
	"os"
	"sort"
	"strings"

	powernap "github.com/charmbracelet/x/powernap/pkg/lsp"
	"github.com/charmbracelet/x/powernap/pkg/lsp/protocol"
)

func applyTextEdits(uri protocol.DocumentURI, edits []protocol.TextEdit, encoding powernap.OffsetEncoding) error {
	path, err := uri.Path()
	if err != nil {
		return fmt.Errorf("invalid URI: %w", err)
	}

	// Read the file content
	content, err := os.ReadFile(path)
	if err != nil {
		return fmt.Errorf("failed to read file: %w", err)
	}

	// Detect line ending style
	var lineEnding string
	if bytes.Contains(content, []byte("\r\n")) {
		lineEnding = "\r\n"
	} else {
		lineEnding = "\n"
	}

	// Track if file ends with a newline
	endsWithNewline := len(content) > 0 && bytes.HasSuffix(content, []byte(lineEnding))

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Filter workspace edit URIs and skip any whose scheme is not file:// before applying.
  2. Check the server's edit payload (log the URI) to identify non-file resources.
  3. Configure/upgrade the LSP server so edits only reference real file URIs.
  4. Convert the resource to a file-backed document (save untitled buffers) before applying edits.

Example fix

// before
for _, edit := range wsEdit.Changes {
    applyTextEdits(edit.Uri, edit.Edits, enc) // may be non-file URI
}

// after
if !strings.HasPrefix(string(edit.Uri), "file://") {
    continue // skip non-file URIs
}
applyTextEdits(edit.Uri, edit.Edits, enc)
Defensive patterns

Strategy: type-guard

Validate before calling

u, err := url.Parse(string(uri))
if err != nil || u.Scheme != "file" {
    return fmt.Errorf("skipping non-file URI: %s", uri)
}

Type guard

func isFileURI(uri protocol.DocumentURI) bool {
    u, err := url.Parse(string(uri))
    return err == nil && u.Scheme == "file"
}

Try / catch

if err := applyWorkspaceEdit(ctx, wsEdit); err != nil {
    if strings.Contains(err.Error(), "invalid URI") {
        slog.Warn("Workspace edit contained a non-file URI; skipped", "error", err)
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: applyTextEdits called (via applyDocumentChange or ApplyWorkspaceEdit) with a URI whose .Path() fails — e.g. an unscheme:// URI, a malformed URI from the server, or a URI for a non-file resource the client cannot map to disk.

Common situations: Server returns workspace edits referencing virtual/unsaved documents; URI strings copied from other tooling without file:// scheme; server bug emitting non-file URIs (e.g. untitled: buffers) in edits; version mismatch where the server encodes URIs unexpectedly.

Related errors


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