charmbracelet/crush · error

error listing directory: %w

Error message

error listing directory: %w

What it means

After the existence check, ListDirectoryTree delegates to fsext.ListDirectory. Any walk/read error from that call is wrapped as 'error listing directory', preserving the underlying cause (permission denied, symlink loops, I/O errors).

Source

Thrown at internal/agent/tools/ls.go:152

		},
	)
}

func ListDirectoryTree(searchPath string, params LSParams, lsConfig config.ToolLs) (string, LSResponseMetadata, error) {
	if _, err := os.Stat(searchPath); os.IsNotExist(err) {
		return "", LSResponseMetadata{}, fmt.Errorf("path does not exist: %s", searchPath)
	}

	depth, limit := lsConfig.Limits()
	maxFiles := cmp.Or(limit, maxLSFiles)
	files, truncated, err := fsext.ListDirectory(
		searchPath,
		params.Ignore,
		cmp.Or(params.Depth, depth),
		maxFiles,
	)
	if err != nil {
		return "", LSResponseMetadata{}, fmt.Errorf("error listing directory: %w", err)
	}

	metadata := LSResponseMetadata{
		NumberOfFiles: len(files),
		Truncated:     truncated,
	}
	tree := createFileTree(files, searchPath)

	var output string
	if truncated {
		output = fmt.Sprintf("There are more than %d files in the directory. Use a more specific path or use the Glob tool to find specific files. The first %[1]d files and directories are included below.\n", maxFiles)
	}
	if depth > 0 {
		output = fmt.Sprintf("The directory tree is shown up to a depth of %d. Use a higher depth and a specific path to see more levels.\n", cmp.Or(params.Depth, depth))
	}
	return output + "\n" + printTree(tree, searchPath), metadata, nil
}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Inspect the wrapped %w error to find the root cause (permission vs I/O).
  2. Fix filesystem permissions on the target directory (chmod/chown) or run as an appropriate user.
  3. Confirm the path is a readable directory, not a special file.
  4. Retry if the failure was transient (e.g. network mount).

Example fix

// before
tree, _, _ := ListDirectoryTree("/root/secret", params, cfg)
// after
// chmod +rx the directory or choose a readable path first
Defensive patterns

Strategy: try-catch

Validate before calling

if fi, err := os.Stat(searchPath); err == nil && !fi.IsDir() {
    return errors.New("not a directory")
}

Type guard

null

Try / catch

tree, meta, err := ListDirectoryTree(p, params, cfg)
if err != nil {
    var perr *fs.PathError
    if errors.As(err, &perr) { /* handle permission/I/O root cause */ }
}

Prevention

When it happens

Trigger: Calling ListDirectoryTree on an existing path where fsext.ListDirectory fails, e.g. the process lacks read permission, the path is a file not a directory in edge cases, or the walker hits an unreadable subdirectory treated as fatal.

Common situations: Directories with restrictive modes (0700 owned by another user), encrypted/mounted volumes that return I/O errors, or sandboxed environments blocking traversal.

Related errors


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