yiisoft/yii2 · error · yii\base\InvalidArgumentException

Unable to change ownership, "{$path}" is not a file or direc

Error message

Unable to change ownership, "{$path}" is not a file or directory.

What it means

FileHelper::changeOwnership() validates its target before touching anything: file_exists((string)$path) is the very first check, and a path that does not exist throws InvalidArgumentException. All later steps (chmod/chown/chgrp) assume a real filesystem node, so the guard prevents operating on typo'd paths, deleted files, or relative paths that resolved from the wrong working directory.

Source

Thrown at framework/helpers/BaseFileHelper.php:976

     * @param string $path the path to the file or directory.
     * @param string|array|int|null $ownership the user and/or group ownership for the file or directory.
     * When $ownership is a string, the format is 'user:group' where both are optional. E.g.
     * 'user' or 'user:' will only change the user,
     * ':group' will only change the group,
     * 'user:group' will change both.
     * When $owners is an index array the format is [0 => user, 1 => group], e.g. `[$myUser, $myGroup]`.
     * It is also possible to pass an associative array, e.g. ['user' => $myUser, 'group' => $myGroup].
     * In case $owners is an integer it will be used as user id.
     * If `null`, an empty array or an empty string is passed, the ownership will not be changed.
     * @param int|null $mode the permission to be set for the file or directory.
     * If `null` is passed, the mode will not be changed.
     *
     * @since 2.0.43
     */
    public static function changeOwnership($path, $ownership, $mode = null)
    {
        if (!file_exists((string)$path)) {
            throw new InvalidArgumentException('Unable to change ownership, "' . $path . '" is not a file or directory.');
        }

        if (empty($ownership) && $ownership !== 0 && $mode === null) {
            return;
        }

        $user = $group = null;
        if (!empty($ownership) || $ownership === 0 || $ownership === '0') {
            if (is_int($ownership)) {
                $user = $ownership;
            } elseif (is_string($ownership)) {
                $ownerParts = explode(':', $ownership);
                $user = $ownerParts[0];
                if (count($ownerParts) > 1) {
                    $group = $ownerParts[1];
                }
            } elseif (is_array($ownership)) {
                $ownershipIsIndexed = ArrayHelper::isIndexed($ownership);

View on GitHub (pinned to 66f00d18a2)

Solutions

  1. Check file_exists($path) (or is_file/is_dir for stricter intent) immediately before the call.
  2. Build paths with Yii::getAlias() and rtrim($base, '/') . '/' . $name instead of bare concatenation.
  3. Create or move the file first, then change ownership as a separate final step.
  4. If the file is optional, skip the call rather than letting it throw.

Example fix

// before
\yii\helpers\FileHelper::changeOwnership($tmpPath, 'www-data:www-data', 0644);

// after
if (is_file($tmpPath)) {
    \yii\helpers\FileHelper::changeOwnership($tmpPath, 'www-data:www-data', 0644);
}
Defensive patterns

Strategy: validation

Validate before calling

if (!file_exists($path)) {
    throw new \RuntimeException("Cannot change ownership — path does not exist: {$path}");
}
\yii\helpers\FileHelper::changeOwnership($path, 'www-data:www-data', 0644);

Try / catch

try {
    \yii\helpers\FileHelper::changeOwnership($path, $ownership, $mode);
} catch (\yii\base\InvalidArgumentException $e) {
    \Yii::warning("changeOwnership rejected: {$e->getMessage()}", 'files');
}

Prevention

When it happens

Trigger: changeOwnership('/srv/app/missing.txt', 'www-data') for a path with a typo or missing separator; a temp file already moved/deleted by another handler before the call; a relative path resolved against CLI cwd instead of the app root; a race with a cleanup job that removed the file.

Common situations: Post-upload permission fixups where the uploaded file was renamed/moved by a previous step; deployment scripts referencing files not yet created; paths built by naive concatenation ('/var/www' . 'app.css'); running the same code in different containers with different volume layouts.

Related errors


AI-assisted analysis of yiisoft/yii2@66f00d18a2 (2026-08-17). Data as JSON: /api/errors/bc2889ba350ec74c. Report an issue: GitHub.