rectorphp/rector · error · ShouldNotHappenException

Matching file paths by using glob-patterns is no longer supp

Error message

Matching file paths by using glob-patterns is no longer supported. Use specific file path instead.

What it means

RectorConfig::import() loads another config file and executes its returned closure; glob/wildcard imports were removed, so it rejects any path that both fails realpath() and contains '*' with ShouldNotHappenException (see upstream issue #9156). The message tells you to import concrete files instead. Every other file type still works -- only wildcard patterns are refused.

Source

Thrown at src/Config/RectorConfig.php:239

            $this->import($configFile);
        }
    }
    /**
     * @param class-string<Command> $commandClass
     */
    public function command(string $commandClass): void
    {
        $this->singleton($commandClass);
        $this->tag($commandClass, Command::class);
    }
    public function import(string $filePath): void
    {
        /**
         * Only stop when filePath realpath is false and contains glob patterns
         * @see https://github.com/rectorphp/rector/issues/9156#issuecomment-2869130541
         */
        if (realpath($filePath) === \false && strpos($filePath, '*') !== \false) {
            throw new ShouldNotHappenException('Matching file paths by using glob-patterns is no longer supported. Use specific file path instead.');
        }
        Assert::fileExists($filePath);
        $self = $this;
        $callable = require $filePath;
        Assert::isCallable($callable);
        /** @var callable(Container $container): void $callable */
        $callable($self);
    }
    /**
     * @param array<class-string<RectorInterface>> $rectorClasses
     */
    public function rules(array $rectorClasses): void
    {
        Assert::allString($rectorClasses);
        RectorConfigValidator::ensureNoDuplicatedClasses($rectorClasses);
        foreach ($rectorClasses as $rectorClass) {
            $this->rule($rectorClass);
        }

View on GitHub (pinned to 408fcb0ff1)

Solutions

  1. Import each file explicitly: ->import(__DIR__ . '/config/early.php')
  2. Or expand the glob yourself and import every existing file (see example)
  3. Prefer native builder composition: put shared rules in a class or use ->withSets()/->import() on concrete files

Example fix

// before
return RectorConfig::configure()
    ->import(__DIR__ . '/config/*.php');

// after
$rectorConfig = RectorConfig::configure();
foreach (glob(__DIR__ . '/config/*.php') as $configFile) {
    $rectorConfig->import($configFile);
}
Defensive patterns

Strategy: validation

Validate before calling

// expand globs yourself so only concrete files reach import()
$configFiles = glob(__DIR__ . '/config/*.php') ?: [];
foreach ($configFiles as $configFile) {
    if (! is_file($configFile)) {
        throw new InvalidArgumentException('Config file vanished: ' . $configFile);
    }
    $rectorConfig->import($configFile);
}

Type guard

function isImportableConfigFile(string $path): bool
{
    return strpos($path, '*') === false && is_file($path);
}

Prevention

When it happens

Trigger: Writing ->import(__DIR__ . '/config/*.php') or ->import(__DIR__ . '/sets/*.php') inside rector.php so multiple sub-configs can be pulled in at once.

Common situations: Configs migrated from other tools (PHPStan/ ECS) or older conventions that allowed glob imports; monorepos trying to auto-discover per-package rule files.

Related errors


AI-assisted analysis of rectorphp/rector@408fcb0ff1 (2026-08-21). Data as JSON: /api/errors/9f168f8c9ffb1b16. Report an issue: GitHub.