symfony/finder · error · Symfony\Component\Finder\Exception\AccessDeniedException

$e->getMessage() (dynamic; wrapped…

Error message

$e->getMessage() (dynamic; wrapped \UnexpectedValueException message, rethrown as AccessDeniedException)

What it means

RecursiveDirectoryIterator::getChildren() wraps any \UnexpectedValueException thrown by the underlying SplFilesystemIterator (typically when a subdirectory cannot be opened or read) into Symfony's AccessDeniedException, preserving message, code, and previous exception. The library throws it so consumers can distinguish permission/unreadable-directory failures during filesystem traversal.

Solutions

  1. Catch AccessDeniedException (or use the ignoreUnreadableDirs constructor flag / Finder's ignoreUnreadableDirs()) so iteration skips unreadable directories
  2. Fix filesystem permissions (chmod/chown) so the iterating user can read the affected directory
  3. Check that the directory still exists before descending; re-stat or refresh the iterator
  4. Inspect the wrapped previous exception to confirm it is an UnexpectedValueException from SplFilesystemIterator open failure

Example fix

// before
foreach ($iterator as $file) {
    $children = $iterator->getChildren(); // throws AccessDeniedException
}
// after
$iterator = new \Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator($path, $flags, true); // ignoreUnreadableDirs
// or
try {
    $children = $iterator->getChildren();
} catch (AccessDeniedException $e) {
    // skip unreadable directory
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!is_dir($dir) || !is_readable($dir)) { /* skip or fix perms */ }

Type guard

function isReadableDir(string $path): bool { return is_dir($path) && is_readable($path); }

Try / catch

try { $children = $iterator->getChildren(); } catch (\Symfony\Component\Finder\Exception\AccessDeniedException $e) { // skip unreadable dir }

Prevention

When it happens

Trigger: Calling getChildren() on a directory entry whose underlying open fails: unreadable directory permissions, a directory removed between iteration steps, or an entry that disappears during iteration (race with concurrent deletion).

Common situations: Scanning vendor or cache directories with restrictive permissions, iterating directories changed by other processes (build cleanup, tmp reaping), running as a user without read access to nested directories, containers with read-only mounts.

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 symfony/finder@4d6c057bfd (2026-09-13). Data as JSON: /api/errors/37c03b044e3742a2. Report an issue: GitHub.

Appendix: source

Thrown at Iterator/RecursiveDirectoryIterator.php:111

    /**
     * @throws AccessDeniedException
     */
    public function getChildren(): \RecursiveDirectoryIterator
    {
        try {
            $children = parent::getChildren();

            if ($children instanceof self) {
                // parent method will call the constructor with default arguments, so unreadable dirs won't be ignored anymore
                $children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs;

                // performance optimization to avoid redoing the same work in all children
                $children->rootPath = $this->rootPath;
            }

            return $children;
        } catch (\UnexpectedValueException $e) {
            throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e);
        }
    }

    public function next(): void
    {
        $this->ignoreFirstRewind = false;

        parent::next();
    }

    public function rewind(): void
    {
        // some streams like FTP are not rewindable, ignore the first rewind after creation,
        // as newly created DirectoryIterator does not need to be rewound
        if ($this->ignoreFirstRewind) {
            $this->ignoreFirstRewind = false;

            return;

View on GitHub (pinned to 4d6c057bfd)