charmbracelet/crush · error
failed to read file: %w
Error message
failed to read file: %w
What it means
This error is returned by the lsp_replace_symbol tool when os.ReadFile cannot read the target file at params.FilePath. The LSP located the symbol and computed its range, but the on-disk file could not be read, so the tool aborts before performing the replacement. The wrapped OS error (%w) tells the real cause: missing file, permissions, or a path that is not a regular file.
Source
Thrown at internal/agent/tools/lsp_replace_symbol.go:97
if client == nil {
return fantasy.NewTextErrorResponse(fmt.Sprintf("no LSP client handles file: %s", params.FilePath)), nil
}
symbols, err := client.DocumentSymbols(ctx, params.FilePath)
if err != nil {
return fantasy.NewTextErrorResponse(fmt.Sprintf("failed to get document symbols: %s", err)), nil
}
target := findSymbolByName(symbols, params.Symbol)
if target == nil {
return fantasy.NewTextErrorResponse(fmt.Sprintf("symbol '%s' not found in %s", params.Symbol, params.FilePath)), nil
}
rng := target.GetRange()
content, err := os.ReadFile(params.FilePath)
if err != nil {
return fantasy.ToolResponse{}, fmt.Errorf("failed to read file: %w", err)
}
lines := strings.Split(string(content), "\n")
startLine := int(rng.Start.Line)
endLine := int(rng.End.Line)
if startLine >= len(lines) || endLine >= len(lines) {
return fantasy.NewTextErrorResponse("symbol range exceeds file length"), nil
}
// Compute new content before permission so the dialog can show a diff.
var newLines []string
switch action {
case "replace":
newLines = make([]string, 0, len(lines))
newLines = append(newLines, lines[:startLine]...)
newLines = append(newLines, strings.Split(params.Replacement, "\n")...)
newLines = append(newLines, lines[endLine+1:]...)
case "add_before":View on GitHub (pinned to 7944b8e522)
Solutions
- Verify the file exists and is readable: run ls -l on params.FilePath and check the path is absolute and correct.
- Re-run the tool after refreshing state so the LSP re-resolves the symbol against the current filesystem.
- Fix file permissions (chmod) or run as a user with read access to the file.
- If the file was deleted or moved, restore it or update the symbol location.
Example fix
// before
content, err := os.ReadFile(params.FilePath)
if err != nil {
return fantasy.ToolResponse{}, fmt.Errorf("failed to read file: %w", err)
}
// after
if _, statErr := os.Stat(params.FilePath); statErr != nil {
return fantasy.ToolResponse{}, fmt.Errorf("file not found before read: %w", statErr)
}
content, err := os.ReadFile(params.FilePath)
if err != nil {
return fantasy.ToolResponse{}, fmt.Errorf("failed to read file: %w", err)
} Defensive patterns
Strategy: validation
Validate before calling
if info, err := os.Stat(filePath); err != nil || info.IsDir() {
return fmt.Errorf("cannot replace symbol: file %s unreadable: %w", filePath, err)
} Try / catch
content, err := os.ReadFile(path)
if err != nil {
var perr *fs.PathError
if errors.As(err, &perr) && errors.Is(err, fs.ErrNotExist) {
// handle missing file: refresh LSP index or skip
}
return err
} Prevention
- Always pass absolute paths to symbol-replacement tools.
- Re-resolve symbol locations against the current filesystem before editing.
- Check file readability (os.Stat + permissions) before invoking LSP edit tools.
- Avoid editing files inside read-only or unmounted paths.
When it happens
Trigger: Calling the lsp_replace_symbol tool with a FilePath that does not exist, has been deleted after LSP analysis, is a directory, or is unreadable due to file permissions (or the process lacks read access, e.g. root-owned file).
Common situations: Symbol reference resolved against a stale index after the file was deleted/renamed; relative path passed where an absolute path is expected; running crush in a container/sandbox where the file is not mounted; file permissions changed by another process.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- failed to write file: %w
- failed to read file: %w
- failed to write file: %w
- error resolving working directory: %w
- error resolving file path: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/83e77e56872c6d83.
Report an issue: GitHub.