symfony/finder · error · RuntimeException

The "ignoreVCSIgnored" option cannot be used by the Finder…

Error message

The "ignoreVCSIgnored" option cannot be used by the Finder as the "{$path}" file is not readable.

What it means

Symfony Finder's `ignoreVCSIgnored()` option filters files using .gitignore rules. While iterating, `VcsIgnoredFilterIterator::isIgnored()` walks the file's parent directories and calls `readGitignoreFile()` to parse each `.gitignore` it finds. When a `.gitignore` path exists but is not a regular readable file (e.g. a directory, or a file the PHP process lacks permission to read), the filter throws this RuntimeException because the ignore rules cannot be evaluated safely.

Solutions

  1. Check the reported path: `ls -la` it — if `.gitignore` is a directory, a broken symlink, or has unreadable permissions, remove or fix it (`chmod u+r .gitignore`, `rm -rf .gitignore` if it is a directory).
  2. If the file must stay restricted, run the PHP process as a user with read access to all `.gitignore` files under the scanned tree.
  3. If you don't need gitignore semantics, drop `->ignoreVCSIgnored()` from the Finder configuration so the filter is never attached.
  4. Pre-clean the scanned tree: ensure every ancestor directory up to the VCS root contains a readable regular-file `.gitignore` or none at all.
  5. Wrap iteration in try/catch for \RuntimeException and fall back to a Finder without ignoreVCSIgnored.

Example fix

// before
$finder = Finder::create()
    ->in($projectDir)
    ->ignoreVCSIgnored(true);

// after: verify readability up front, or disable the option
foreach (Finder::create()->directories()->in($projectDir) as $dir) {
    $gi = $dir->getRealPath() . '/.gitignore';
    if (file_exists($gi) && (!is_file($gi) || !is_readable($gi))) {
        chmod($gi, 0644); // or skip handling manually
    }
}
$finder = Finder::create()
    ->in($projectDir)
    ->ignoreVCSIgnored(true);
Defensive patterns

Strategy: try-catch

Validate before calling

function ensureGitignoresReadable(string $dir): void {
    $it = new \RecursiveIteratorIterator(
        new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS)
    );
    foreach ($it as $f) {
        if ($f->getFilename() === '.gitignore'
            && (!is_file($f->getPathname()) || !is_readable($f->getPathname()))) {
            throw new \RuntimeException("Unreadable .gitignore: " . $f->getPathname());
        }
    }
}
ensureGitignoresReadable($projectDir);

Type guard

function isReadableGitignore(string $path): bool {
    return file_exists($path) && is_file($path) && is_readable($path);
}

// usage before iterating:
// if (!isReadableGitignore($baseDir . '/.gitignore')) { /* fix perms or skip */ }

Try / catch

try {
    $files = iterator_to_array(
        Finder::create()->in($dir)->ignoreVCSIgnored(true)->files()
    );
} catch (\RuntimeException $e) {
    if (str_contains($e->getMessage(), 'ignoreVCSIgnored')) {
        $files = iterator_to_array(
            Finder::create()->in($dir)->files() // fallback: no gitignore filtering
        );
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Using `$finder->ignoreVCSIgnored(true)` (or ignoreDotFiles interplay) while iterating files under a tree where a directory-level `.gitignore` path exists but `is_file()` returns false or `is_readable()` returns false — e.g. `.gitignore` is a directory, a symlink to an unreadable target, has restrictive permissions (chmod 000 / root-owned in a container running as non-root), or open_basedir restrictions make it unreadable.

Common situations: Docker containers running Finder as www-data while `.gitignore` is owned by root with 0644/0600; a build step accidentally creating `.gitignore` as a directory; broken symlinks named `.gitignore`; CI environments with hardened permissions; NFS/Windows mounts where readability checks fail; macOS/Linux permission differences after `git clean` or template scaffolding.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of symfony/finder@4d6c057bfd (2026-09-13). Data as JSON: /api/errors/14c30faab1de5fd4. Report an issue: GitHub.

Appendix: source

Thrown at Iterator/VcsIgnoredFilterIterator.php:159

    }

    /**
     * Returns the rules of a .gitignore file, last one first, as [regex, isNegated, isDirOnly] tuples.
     *
     * @return list<array{0: string, 1: bool, 2: bool}>|null
     */
    private function readGitignoreFile(string $path): ?array
    {
        if (\array_key_exists($path, $this->gitignoreFilesCache)) {
            return $this->gitignoreFilesCache[$path];
        }

        if (!file_exists($path)) {
            return $this->gitignoreFilesCache[$path] = null;
        }

        if (!is_file($path) || !is_readable($path)) {
            throw new \RuntimeException("The \"ignoreVCSIgnored\" option cannot be used by the Finder as the \"{$path}\" file is not readable.");
        }

        $rules = [];

        foreach (preg_split('~\r\n?|\n~', file_get_contents($path)) as $line) {
            // only a line starting with "#" is a comment, and only trailing spaces are stripped
            if (str_starts_with($line, '#')) {
                continue;
            }

            $line = preg_replace('~(?<!\\\\) +$~', '', $line);

            if ($isNegated = str_starts_with($line, '!')) {
                $line = substr($line, 1);
            }

            if ($isDirOnly = str_ends_with($line, '/')) {
                $line = substr($line, 0, -1);

View on GitHub (pinned to 4d6c057bfd)