rectorphp/rector · error · ShouldNotHappenException

Bootstrap file "%s" does not exist.

Error message

Bootstrap file "%s" does not exist.

What it means

BootstrapFilesIncluder loads every file listed in the BOOTSTRAP_FILES parameter (set by ->withBootstrapFiles([...]) in rector.php or --bootstrap-file on the CLI) before analysis starts, so user code needed by custom rules is loaded. Each path is checked with is_file() and a missing one aborts immediately with ShouldNotHappenException. Relative paths are resolved against the current working directory, which is the usual culprit.

Source

Thrown at src/Autoloading/BootstrapFilesIncluder.php:30

use SplFileInfo;
use RectorPrefix202608\Webmozart\Assert\Assert;
/**
 * @see \Rector\Tests\Autoloading\BootstrapFilesIncluderTest
 */
final class BootstrapFilesIncluder
{
    /**
     * Inspired by
     * @see https://github.com/phpstan/phpstan-src/commit/aad1bf888ab7b5808898ee5fe2228bb8bb4e4cf1
     */
    public function includeBootstrapFiles(Container $container): void
    {
        $bootstrapFiles = SimpleParameterProvider::provideArrayParameter(Option::BOOTSTRAP_FILES);
        Assert::allString($bootstrapFiles);
        /** @var string[] $bootstrapFiles */
        foreach ($bootstrapFiles as $bootstrapFile) {
            if (!is_file($bootstrapFile)) {
                throw new ShouldNotHappenException(sprintf('Bootstrap file "%s" does not exist.', $bootstrapFile));
            }
            // mimic PHPStan bootstrap file inclusion (bootstrap files have access to the global $container variable)
            (static function (string $file) use ($container): void {
                require $file;
            })($bootstrapFile);
        }
        $this->requireRectorStubs();
    }
    private function requireRectorStubs(): void
    {
        $stubsRectorDirectory = realpath(__DIR__ . '/../../stubs-rector');
        if ($stubsRectorDirectory === \false) {
            return;
        }
        $dir = new RecursiveDirectoryIterator($stubsRectorDirectory, RecursiveDirectoryIterator::SKIP_DOTS);
        $stubs = new RecursiveIteratorIterator($dir);
        foreach ($stubs as $stub) {
            /** @var SplFileInfo $stub */

View on GitHub (pinned to 408fcb0ff1)

Solutions

  1. Use absolute paths anchored on the config file: ->withBootstrapFiles([__DIR__ . '/rector-bootstrap.php'])
  2. Verify the file exists: ls the exact path from the directory where you run rector
  3. If you run rector from another directory, cd to the project root or fix the relative path accordingly

Example fix

// before: relative path breaks when cwd differs
return RectorConfig::configure()
    ->withBootstrapFiles(['bootstrap/rector-bootstrap.php']);

// after: absolute path relative to the config file
return RectorConfig::configure()
    ->withBootstrapFiles([__DIR__ . '/bootstrap/rector-bootstrap.php']);
Defensive patterns

Strategy: validation

Validate before calling

// in rector.php, before registering
$bootstrapFile = __DIR__ . '/bootstrap/rector-bootstrap.php';
if (! is_file($bootstrapFile)) {
    throw new InvalidArgumentException('Bootstrap file missing: ' . $bootstrapFile);
}
return RectorConfig::configure()->withBootstrapFiles([$bootstrapFile]);

Type guard

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

Prevention

When it happens

Trigger: Calling ->withBootstrapFiles(['bootstrap/rector-bootstrap.php']) or --bootstrap-file=boot.php where the path is relative and rector runs from a different cwd (e.g. from a subdirectory, via a global binary, or in CI with a different working directory), or the file was simply deleted/renamed.

Common situations: Monorepo where rector runs at the repo root but rector.php lives in a package and uses relative paths; CI checking out the config without the bootstrap file; typos in the path.

Related errors


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