{"record":{"id":"f5100b4b4ef8e561","repo":"charmbracelet/crush","slug":"error-checking-file-w","errorCode":null,"errorMessage":"error checking file: %w","messagePattern":"error checking file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/write.go","lineNumber":86,"sourceCode":"\t\t\tfileInfo, err := os.Stat(filePath)\n\t\t\tif err == nil {\n\t\t\t\tif fileInfo.IsDir() {\n\t\t\t\t\treturn fantasy.NewTextErrorResponse(fmt.Sprintf(\"Path is a directory, not a file: %s\", filePath)), nil\n\t\t\t\t}\n\n\t\t\t\tmodTime := fileInfo.ModTime().Truncate(time.Second)\n\t\t\t\tlastRead := filetracker.LastReadTime(ctx, sessionID, filePath)\n\t\t\t\tif modTime.After(lastRead) {\n\t\t\t\t\treturn fantasy.NewTextErrorResponse(fmt.Sprintf(\"File %s has been modified since it was last read.\\nLast modification: %s\\nLast read: %s\\n\\nPlease read the file again before modifying it.\",\n\t\t\t\t\t\tfilePath, modTime.Format(time.RFC3339), lastRead.Format(time.RFC3339))), nil\n\t\t\t\t}\n\n\t\t\t\toldContent, readErr := os.ReadFile(filePath)\n\t\t\t\tif readErr == nil && string(oldContent) == params.Content {\n\t\t\t\t\treturn fantasy.NewTextErrorResponse(fmt.Sprintf(\"File %s already contains the exact content. No changes made.\", filePath)), nil\n\t\t\t\t}\n\t\t\t} else if !os.IsNotExist(err) {\n\t\t\t\treturn fantasy.ToolResponse{}, fmt.Errorf(\"error checking file: %w\", err)\n\t\t\t}\n\n\t\t\tdir := filepath.Dir(filePath)\n\t\t\tif err = os.MkdirAll(dir, 0o755); err != nil {\n\t\t\t\treturn fantasy.ToolResponse{}, fmt.Errorf(\"error creating directory: %w\", err)\n\t\t\t}\n\n\t\t\toldContent := \"\"\n\t\t\tif fileInfo != nil && !fileInfo.IsDir() {\n\t\t\t\toldBytes, readErr := os.ReadFile(filePath)\n\t\t\t\tif readErr == nil {\n\t\t\t\t\toldContent = string(oldBytes)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdiff, additions, removals := diff.GenerateDiff(\n\t\t\t\toldContent,\n\t\t\t\tparams.Content,","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/write.go#L68-L104","documentation":"Before writing, the tool calls os.Stat on the target path to compare existing content and skip no-op writes. If Stat fails with an error other than NotExist (permissions, I/O error, path issues), the tool aborts and wraps the OS error. The write never proceeds because the tool cannot safely determine the file's state.","triggerScenarios":"os.Stat(filePath) returns an error that is not os.IsNotExist — e.g. a parent directory in the path is not searchable (chmod 000), the path is on a failing mount, or an intermediate path component is a file, not a directory.","commonSituations":"Writing into a directory the agent user cannot traverse; ENOTDIR from a bad path segment; NFS/overlayfs glitches in containerized environments; SELinux/AppArmor denying access.","solutions":["Check permissions on every directory component of the target path (need +x on dirs).","Verify the path is valid: no intermediate component is a regular file (ls each segment).","If on a network/overlay mount, test Stat from the same user the agent runs as.","As a fallback, choose a writable path (project working dir) instead of one outside it."],"exampleFix":"// before\nerr := tool.Run(ctx, fantasy.ToolCall{Input: `{\"file_path\":\"/etc/hosts.d/app.conf\",\"content\":\"...\"}`})\n\n// after\n// use a path inside the working directory the process can traverse\nerr := tool.Run(ctx, fantasy.ToolCall{Input: `{\"file_path\":\"config/app.conf\",\"content\":\"...\"}`})","handlingStrategy":"try-catch","validationCode":"if _, err := os.Stat(targetPath); err != nil && !os.IsNotExist(err) {\n    return fmt.Errorf(\"cannot access %s: %w\", targetPath, err)\n}","typeGuard":"func statAccessible(path string) bool {\n    _, err := os.Stat(path)\n    return err == nil || os.IsNotExist(err)\n}","tryCatchPattern":"var pathErr *fs.PathError\nif errors.As(err, &pathErr) && errors.Is(err, fs.ErrPermission) {\n    return fmt.Errorf(\"permission denied checking %s; fix directory execute bits\", pathErr.Path)\n}","preventionTips":["Keep agent writes inside the project working directory.","Check directory execute/search permissions for the running user before automating writes.","Avoid paths on read-only or flaky mounts."],"tags":["go","filesystem","permissions","os-stat"],"backgroundTag":"file-stat-failed","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}