sebastianbergmann/phpunit · error · PHPUnit\Util\InvalidDirectoryException
"%s" is not a directory
Error message
"%s" is not a directory
What it means
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.
Source
Thrown at src/Util/ExcludeList.php:153
Tokenizer::class => 1,
];
/**
* @var list<string>
*/
private static array $directories = [];
private static bool $initialized = false;
private readonly bool $enabled;
/**
* @param non-empty-string $directory
*
* @throws InvalidDirectoryException
*/
public static function addDirectory(string $directory): void
{
if (!is_dir($directory)) {
throw new InvalidDirectoryException($directory);
}
$directory = realpath($directory);
assert($directory !== false);
self::$directories[] = $directory;
}
public function __construct(?bool $enabled = null)
{
if ($enabled === null) {
$enabled = !defined('PHPUNIT_TESTSUITE');
}
$this->enabled = $enabled;
}
View on GitHub (pinned to f123cdb2a2)
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
Example fix
// before
\PHPUnit\Util\ExcludeList::addDirectory('src/generated');
// InvalidDirectoryException: "src/generated" is not a directory (cwd differs)
// after
$dir = realpath(__DIR__ . '/src/generated');
if ($dir !== false) {
\PHPUnit\Util\ExcludeList::addDirectory($dir);
} Defensive patterns
Strategy: validation
Validate before calling
use PHPUnit\Util\ExcludeList;
$dir = __DIR__ . '/../generated';
if (is_dir($dir)) {
ExcludeList::addDirectory($dir);
} Type guard
static function isExistingDirectory(string $path): bool
{
return is_dir($path) && !is_link($path) || (is_link($path) && is_dir(readlink($path) ?: ''));
} Try / catch
use PHPUnit\Util\InvalidDirectoryException;
try {
ExcludeList::addDirectory($configuredDir);
} catch (InvalidDirectoryException $e) {
// log and continue: an unused exclusion should not break the suite
error_log('Skipping exclusion: ' . $e->getMessage());
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Cannot open bootstrap script "%s"
- Error in bootstrap script: %s:%s%s%s%s
- Subscriber "%s" does not implement any known interface - did
- Unknown event type "%s"
- Subscriber "%s" does not implement a known interface
AI-assisted analysis of sebastianbergmann/phpunit@f123cdb2a2 (2026-08-23).
Data as JSON: /api/errors/df009a7d46ed2018.
Report an issue: GitHub.