{"record":{"id":"33a361f08d245d3f","repo":"charmbracelet/crush","slug":"failed-to-read-file-w","errorCode":null,"errorMessage":"failed to read file: %w","messagePattern":"failed to read file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/edit.go","lineNumber":307,"sourceCode":"\n\tlastRead := edit.filetracker.LastReadTime(edit.ctx, sessionID, filePath)\n\tif lastRead.IsZero() {\n\t\treturn \"\", \"\", false, fantasy.NewTextErrorResponse(\"you must read the file before editing it. Use the View tool first\"), nil\n\t}\n\n\tmodTime := fileInfo.ModTime().Truncate(time.Second)\n\tif modTime.After(lastRead) {\n\t\treturn \"\", \"\", false, fantasy.NewTextErrorResponse(\n\t\t\tfmt.Sprintf(\n\t\t\t\t\"file %s has been modified since it was last read (mod time: %s, last read: %s)\",\n\t\t\t\tfilePath, modTime.Format(time.RFC3339), lastRead.Format(time.RFC3339),\n\t\t\t),\n\t\t), nil\n\t}\n\n\tcontent, err := os.ReadFile(filePath)\n\tif err != nil {\n\t\treturn \"\", \"\", false, fantasy.ToolResponse{}, fmt.Errorf(\"failed to read file: %w\", err)\n\t}\n\n\toldContent, isCrlf = fsext.ToUnixLineEndings(string(content))\n\treturn sessionID, oldContent, isCrlf, fantasy.ToolResponse{}, nil\n}\n\nfunc deleteContent(edit editContext, filePath, oldString string, replaceAll bool, call fantasy.ToolCall) (fantasy.ToolResponse, error) {\n\tsessionID, oldContent, isCrlf, resp, err := loadExistingFile(edit, filePath, \"session ID is required for deleting content\")\n\tif err != nil {\n\t\treturn fantasy.ToolResponse{}, err\n\t}\n\tif resp.Content != \"\" || resp.IsError {\n\t\treturn resp, nil\n\t}\n\n\tnewContent, whitespaceCorrected, err := findAndReplace(oldContent, oldString, \"\", replaceAll)\n\tif err != nil {\n\t\treturn fantasy.NewTextErrorResponse(err.Error()), nil","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/edit.go#L289-L325","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: file removed between stat and read (TOCTOU)\n// after: make the path stable, or handle retry\ncontent, err := os.ReadFile(filePath)\nif err != nil {\n\tif os.IsNotExist(err) {\n\t\treturn retryWithFreshStat()\n\t}\n\treturn fmt.Errorf(\"failed to read file: %w\", err)\n}","handlingStrategy":"retry","validationCode":"func readableRegularFile(path string) error {\n\tfi, err := os.Stat(path)\n\tif err != nil { return err }\n\tif !fi.Mode().IsRegular() {\n\t\treturn errors.New(\"not a regular file\")\n\t}\n\treturn syscall.Access(path, syscall.O_RDONLY)\n}","typeGuard":null,"tryCatchPattern":"if errors.Is(err, fs.ErrNotExist) || errors.Is(err, syscall.EIO) {\n\t// re-stat and retry once with backoff\n\ttime.Sleep(100 * time.Millisecond)\n\tretry()\n}","preventionTips":["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."],"tags":["filesystem","file-read","edit-tool","race-condition"],"backgroundTag":"file-read-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}