symfony/finder · error · InvalidArgumentException

Invalid PHP callback.

Error message

Invalid PHP callback.

What it means

ExcludeDirectoryFilterIterator treats each entry of $directories as either a directory name string or a prune filter callable; when an entry is neither a string nor callable it throws InvalidArgumentException('Invalid PHP callback.'). This occurs while building the exclusion rules in the constructor.

Solutions

  1. Pass only strings (directory names) or valid callables; flatten the array
  2. Coerce non-string values to strings with (string) or validate each with is_string($d) || is_callable($d) beforehand
  3. Fix config deserialization that injects numeric keys or objects into the exclusion list

Example fix

// before
$finder->exclude([0 => 'vendor', 'cache' => true]); // 'true' boolean entry
// after
$finder->exclude(['vendor', 'cache']);
Defensive patterns

Strategy: type-guard

Validate before calling

foreach ($dirs as $d) { if (!is_string($d) && !is_callable($d)) { throw new \InvalidArgumentException('Exclusion must be a string or callable'); } }

Type guard

function isValidExclusion(mixed $d): bool { return is_string($d) || is_callable($d); }

Try / catch

try { $finder->exclude($dirs); } catch (\InvalidArgumentException $e) { /* handle invalid exclusion entry */ }

Prevention

When it happens

Trigger: Finder::exclude([123, ['not-callable-array'], new \stdClass()]) — passing integers, non-invokable objects, or arrays that aren't valid callables as exclusion entries.

Common situations: Config files listing exclusions with numeric or mixed values, dynamic exclusion lists built from unvalidated input, accidentally passing a nested array of names instead of flat strings.

Understand the failure class

Background: "must be a positive integer", "cannot be empty", "invalid argument": how invalid-argument errors work across open-source libraries — this error's family across 33 libraries.

Related errors


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

Appendix: source

Thrown at Iterator/ExcludeDirectoryFilterIterator.php:50

    private ?string $excludedPattern = null;
    private ?string $excludedRootPattern = null;
    /** @var list<callable(SplFileInfo):bool> */
    private array $pruneFilters = [];

    /**
     * @param \Iterator<string, SplFileInfo>          $iterator    The Iterator to filter
     * @param list<string|callable(SplFileInfo):bool> $directories An array of directories to exclude
     */
    public function __construct(\Iterator $iterator, array $directories)
    {
        $this->iterator = $iterator;
        $this->isRecursive = $iterator instanceof \RecursiveIterator;
        $patterns = [];
        $rootPatterns = [];
        foreach ($directories as $directory) {
            if (!\is_string($directory)) {
                if (!\is_callable($directory)) {
                    throw new \InvalidArgumentException('Invalid PHP callback.');
                }

                $this->pruneFilters[] = $directory;

                continue;
            }

            $directory = rtrim($directory, '/');
            if (str_starts_with($directory, '/')) {
                $rootPatterns[] = preg_quote(substr($directory, 1), '#');
            } elseif (!$this->isRecursive || str_contains($directory, '/')) {
                $patterns[] = preg_quote($directory, '#');
            } else {
                $this->excludedDirs[$directory] = true;
            }
        }
        if ($patterns) {
            $this->excludedPattern = '#(?:^|/)(?:'.implode('|', $patterns).')(?:/|$)#';

View on GitHub (pinned to 4d6c057bfd)