yiisoft/yii2 · error · InvalidArgumentException
The dir argument must be a directory: $dir
Error message
The dir argument must be a directory: $dir
What it means
clearDir() is an internal guard in BaseFileHelper used by directory mutation helpers (removeDirectory() and friends) that throws InvalidArgumentException when is_dir($dir) is false. Its job is to stop destructive operations before they start when the target is a regular file, a dangling symlink, or a path already deleted by someone else. The message names the path so the caller can see exactly what failed the check.
Source
Thrown at framework/helpers/BaseFileHelper.php:650
*/
private static function openDir($dir)
{
$handle = opendir($dir);
if ($handle === false) {
throw new InvalidArgumentException("Unable to open directory: $dir");
}
return $handle;
}
/**
* @param string $dir
* @return string
* @throws InvalidArgumentException if directory not exists
*/
private static function clearDir($dir)
{
if (!is_dir($dir)) {
throw new InvalidArgumentException("The dir argument must be a directory: $dir");
}
return rtrim($dir, '\/');
}
/**
* Checks if the given file path satisfies the filtering options.
* @param string $path the path of the file or directory to be checked
* @param array $options the filtering options. See [[findFiles()]] for explanations of
* the supported options.
* @return bool whether the file or directory satisfies the filtering options.
*/
public static function filterPath($path, $options)
{
if (isset($options['filter'])) {
$result = call_user_func($options['filter'], $path);
if (is_bool($result)) {
return $result;
}View on GitHub (pinned to 66f00d18a2)
Solutions
- Guard the call: if (is_dir($path)) { FileHelper::removeDirectory($path); } so cleanup is idempotent.
- Resolve the path first with realpath($path) to collapse symlinks and verify what is really on disk.
- Fix the upstream path bug — make sure the variable holds the directory (dirname()) rather than a file inside it.
- If the directory must exist afterwards, recreate it with mkdir($path, 0775, true) after removal.
Example fix
// before
\yii\helpers\FileHelper::removeDirectory(\Yii::getAlias('@runtime/cache/segments'));
// after
$dir = \Yii::getAlias('@runtime/cache/segments');
if (is_dir($dir)) {
\yii\helpers\FileHelper::removeDirectory($dir);
} Defensive patterns
Strategy: validation
Validate before calling
if (!is_dir($dir)) {
return; // nothing to clean — missing path or a regular file
}
\yii\helpers\FileHelper::removeDirectory($dir); Type guard
/** Narrow a path to an existing directory, or null. */
function toExistingDir(string $path): ?string
{
$real = realpath($path);
return ($real !== false && is_dir($real)) ? $real : null;
} Try / catch
try {
\yii\helpers\FileHelper::removeDirectory($dir);
} catch (\yii\base\InvalidArgumentException $e) {
\Yii::warning("Cleanup skipped: {$e->getMessage()}", 'filehelper');
} Prevention
- Treat directory cleanup as idempotent — never assume the directory survived since the last check.
- Resolve user-supplied paths with realpath() before destructive calls.
- Keep file paths and their parent directory paths in separate, clearly named variables.
When it happens
Trigger: FileHelper::removeDirectory($path) or copyDirectory() where $path is actually a regular file; a symlink whose target was deleted (is_dir() on a dead symlink returns false); the directory being removed by a concurrent process between the caller's check and the call; a path variable accidentally holding an uploaded file's path instead of its parent directory.
Common situations: Cleanup cron jobs racing with concurrent deletes; file-upload handlers mixing file paths and directory paths in the same variable; dangling symlinks in shared temp directories; test fixtures creating a file where the code expects a directory.
Related errors
- Unable to open directory: $dir
- Unknown scenario: $scenario
- Invalid validation rule: a rule must specify both attribute
- FileDependency::fileName must be set
- Operator '$operator' requires three operands.
AI-assisted analysis of yiisoft/yii2@66f00d18a2 (2026-08-17).
Data as JSON: /api/errors/68fa6cd7af67beb5.
Report an issue: GitHub.