charmbracelet/crush · error

error accessing file: %w

Error message

error accessing file: %w

What it means

os.Stat(filePath) failed with an error other than NotExist (those become friendly "File not found" text responses with suggestions). The wrapped errno tells you what else went wrong — permission denied on a path component, too many symlinks, I/O error, etc.

Source

Thrown at internal/agent/tools/view.go:186

						for _, entry := range dirEntries {
							if strings.Contains(strings.ToLower(entry.Name()), strings.ToLower(base)) ||
								strings.Contains(strings.ToLower(base), strings.ToLower(entry.Name())) {
								suggestions = append(suggestions, filepath.Join(dir, entry.Name()))
								if len(suggestions) >= 3 {
									break
								}
							}
						}

						if len(suggestions) > 0 {
							return fantasy.NewTextErrorResponse(fmt.Sprintf("File not found: %s\n\nDid you mean one of these?\n%s",
								filePath, strings.Join(suggestions, "\n"))), nil
						}
					}

					return fantasy.NewTextErrorResponse(fmt.Sprintf("File not found: %s", filePath)), nil
				}
				return fantasy.ToolResponse{}, fmt.Errorf("error accessing file: %w", err)
			}

			// Check if it's a directory
			if fileInfo.IsDir() {
				return fantasy.NewTextErrorResponse(fmt.Sprintf("Path is a directory, not a file: %s", filePath)), nil
			}

			// Set default limit if not provided (no limit for SKILL.md files)
			if params.Limit <= 0 {
				if isSkillFile {
					params.Limit = 1000000 // Effectively no limit for skill files
				} else {
					params.Limit = DefaultReadLimit
				}
			}

			isSupportedImage, mimeType := getImageMimeType(filePath)
			if isSupportedImage {

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Read the wrapped cause: EACCES → fix permissions (chmod/chown or run with access); ELOOP → fix the symlink; ENOTDIR → correct the path
  2. Verify each path component with ls/stat as the same user the process runs as
  3. Pick a readable copy of the file instead of the protected one
  4. If outside the workdir, ensure the read permission request was granted — though denial normally yields a different response

Example fix

// before
{"file_path":"/root/.ssh/id_rsa"} // stat: permission denied
// after
chmod o+r /path/to/readable/file  # or read a copy the user can access
{"file_path":"/home/user/project/file.txt"}
Defensive patterns

Strategy: fallback

Validate before calling

if _, err := os.Stat(path); err != nil && !os.IsNotExist(err) {
    // EACCES/ELOOP/ENOTDIR etc. — resolve before calling the tool
    return fmt.Errorf("path %q not accessible: %w", path, err)
}

Type guard

func isAccessibleFile(path string) bool {
    st, err := os.Stat(path)
    return err == nil && !st.IsDir()
}

Try / catch

var pe *fs.PathError
if errors.As(err, &pe) {
    switch pe.Err {
    case syscall.EACCES: // fix permissions / read an accessible copy
    case syscall.ELOOP:  // fix symlink loop
    case syscall.ENOTDIR: // correct the path
    }
}

Prevention

When it happens

Trigger: Reading a file whose parent directory (or the file) lacks read/search permission (EACCES), a symlink loop (ELOOP), a path component that is not a directory (ENOTDIR), or device I/O errors.

Common situations: Files under directories with restrictive permissions (root-owned, SSH keys, system dirs); broken mount points; macOS/SELinux sandbox denials; paths with a regular file where a directory is expected.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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