charmbracelet/crush · error
failed to read file: %w
Error message
failed to read file: %w
What it means
After the stat succeeds, loadExistingFile reads the file with os.ReadFile and wraps any read failure in this error. The file exists and is not a directory, but the bytes could not be read at the moment of reading.
Source
Thrown at internal/agent/tools/edit.go:307
lastRead := edit.filetracker.LastReadTime(edit.ctx, sessionID, filePath)
if lastRead.IsZero() {
return "", "", false, fantasy.NewTextErrorResponse("you must read the file before editing it. Use the View tool first"), nil
}
modTime := fileInfo.ModTime().Truncate(time.Second)
if modTime.After(lastRead) {
return "", "", false, fantasy.NewTextErrorResponse(
fmt.Sprintf(
"file %s has been modified since it was last read (mod time: %s, last read: %s)",
filePath, modTime.Format(time.RFC3339), lastRead.Format(time.RFC3339),
),
), nil
}
content, err := os.ReadFile(filePath)
if err != nil {
return "", "", false, fantasy.ToolResponse{}, fmt.Errorf("failed to read file: %w", err)
}
oldContent, isCrlf = fsext.ToUnixLineEndings(string(content))
return sessionID, oldContent, isCrlf, fantasy.ToolResponse{}, nil
}
func deleteContent(edit editContext, filePath, oldString string, replaceAll bool, call fantasy.ToolCall) (fantasy.ToolResponse, error) {
sessionID, oldContent, isCrlf, resp, err := loadExistingFile(edit, filePath, "session ID is required for deleting content")
if err != nil {
return fantasy.ToolResponse{}, err
}
if resp.Content != "" || resp.IsError {
return resp, nil
}
newContent, whitespaceCorrected, err := findAndReplace(oldContent, oldString, "", replaceAll)
if err != nil {
return fantasy.NewTextErrorResponse(err.Error()), nilView on GitHub (pinned to 7944b8e522)
Solutions
- Read the wrapped %w cause to identify open/read failure.
- Re-run the edit if the file changed concurrently (deletion race).
- Check read permissions/ACLs on the file itself.
- Point the edit at a regular file, not devices/sockets/FIFOs.
Example fix
// before: file removed between stat and read (TOCTOU)
// after: make the path stable, or handle retry
content, err := os.ReadFile(filePath)
if err != nil {
if os.IsNotExist(err) {
return retryWithFreshStat()
}
return fmt.Errorf("failed to read file: %w", err)
} Defensive patterns
Strategy: retry
Validate before calling
func readableRegularFile(path string) error {
fi, err := os.Stat(path)
if err != nil { return err }
if !fi.Mode().IsRegular() {
return errors.New("not a regular file")
}
return syscall.Access(path, syscall.O_RDONLY)
} Try / catch
if errors.Is(err, fs.ErrNotExist) || errors.Is(err, syscall.EIO) {
// re-stat and retry once with backoff
time.Sleep(100 * time.Millisecond)
retry()
} Prevention
- Don't delete or replace files while the agent is reading/editing them.
- Edit only regular files; skip devices, sockets, and FIFOs.
- Verify read permissions/ACLs before programmatic edits.
- On network mounts, retry transient I/O errors with backoff.
When it happens
Trigger: os.ReadFile failing after a successful Stat: file deleted between Stat and Read (race), EACCES on open, EIO on the underlying storage, or special files (devices, sockets, FIFOs) that error on read.
Common situations: Another process deletes/replaces the file concurrently, filesystem errors on remote mounts, permission tightened by an ACL after stat, or the path points to /dev or a socket.
Related errors
- failed to write file: %w
- failed to read file: %w
- cannot get ownership for %s: %w
- workspace closing
- failed to create parent directories: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/33a361f08d245d3f.
Report an issue: GitHub.