{"record":{"id":"df009a7d46ed2018","repo":"sebastianbergmann/phpunit","slug":"s-is-not-a-directory","errorCode":null,"errorMessage":"\"%s\" is not a directory","messagePattern":"\"(.+?)\" is not a directory","errorType":"exception","errorClass":"PHPUnit\\Util\\InvalidDirectoryException","httpStatus":null,"severity":"error","filePath":"src/Util/ExcludeList.php","lineNumber":153,"sourceCode":"        Tokenizer::class => 1,\n    ];\n\n    /**\n     * @var list<string>\n     */\n    private static array $directories = [];\n    private static bool $initialized  = false;\n    private readonly bool $enabled;\n\n    /**\n     * @param non-empty-string $directory\n     *\n     * @throws InvalidDirectoryException\n     */\n    public static function addDirectory(string $directory): void\n    {\n        if (!is_dir($directory)) {\n            throw new InvalidDirectoryException($directory);\n        }\n\n        $directory = realpath($directory);\n\n        assert($directory !== false);\n\n        self::$directories[] = $directory;\n    }\n\n    public function __construct(?bool $enabled = null)\n    {\n        if ($enabled === null) {\n            $enabled = !defined('PHPUNIT_TESTSUITE');\n        }\n\n        $this->enabled = $enabled;\n    }\n","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/sebastianbergmann/phpunit/blob/f123cdb2a2d49f15025794166cfed8bda8627dd2/src/Util/ExcludeList.php#L135-L171","documentation":"ExcludeList::addDirectory() is a static API to register a directory whose files PHPUnit's error/deprecation reporting should ignore (commonly used to silence vendor deprecations). It validates its argument with is_dir() and throws InvalidDirectoryException for anything that is not an existing directory. This prevents typo'd or stale paths from silently making the exclusion useless.","triggerScenarios":"Calling ExcludeList::addDirectory($dir) from a bootstrap with $dir that is actually a file, a symlink pointing to a nonexistent target, or a relative path that does not resolve from the current working directory when PHPUnit runs.","commonSituations":"Bootstraps that build paths relative to getcwd() instead of __DIR__; projects moved or renamed after the bootstrap was written; vendor replaced by a symlink in deployment images; phar-based tooling where relative paths change meaning.","solutions":["Pass an absolute path anchored to the bootstrap: ExcludeList::addDirectory(__DIR__ . '/../vendor')","Check is_dir($dir) before calling when the path comes from configuration or user input","Fix or remove stale symlinks and typos in the configured directory","When the directory is optional, wrap the call in a guard so its absence does not break the whole run"],"exampleFix":"// before\n\\PHPUnit\\Util\\ExcludeList::addDirectory('src/generated');\n// InvalidDirectoryException: \"src/generated\" is not a directory (cwd differs)\n\n// after\n$dir = realpath(__DIR__ . '/src/generated');\nif ($dir !== false) {\n    \\PHPUnit\\Util\\ExcludeList::addDirectory($dir);\n}","handlingStrategy":"validation","validationCode":"use PHPUnit\\Util\\ExcludeList;\n\n$dir = __DIR__ . '/../generated';\nif (is_dir($dir)) {\n    ExcludeList::addDirectory($dir);\n}","typeGuard":"static function isExistingDirectory(string $path): bool\n{\n    return is_dir($path) && !is_link($path) || (is_link($path) && is_dir(readlink($path) ?: ''));\n}","tryCatchPattern":"use PHPUnit\\Util\\InvalidDirectoryException;\n\ntry {\n    ExcludeList::addDirectory($configuredDir);\n} catch (InvalidDirectoryException $e) {\n    // log and continue: an unused exclusion should not break the suite\n    error_log('Skipping exclusion: ' . $e->getMessage());\n}","preventionTips":["Always anchor exclusion paths with __DIR__ or realpath(), never getcwd()-relative strings","Derive paths from __DIR__ at bootstrap time, when the working directory is still predictable","Treat optional exclusions as guarded calls: check is_dir() first and log when skipped"],"tags":["phpunit","exclude-list","bootstrap","path-validation"],"backgroundTag":"invalid-directory-path","analyzedSha":"f123cdb2a2d49f15025794166cfed8bda8627dd2","analyzedAt":"2026-08-23T01:20:58.058Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}