yiisoft/yii2 · error · yii\base\Exception

Unable to change user ownership of "{$path}" to "{$user}".

Error message

Unable to change user ownership of "{$path}" to "{$user}".

What it means

When the resolved user passes type checks but chown($path, $user) returns false, changeOwnership() throws yii\base\Exception naming the path and user. On Unix, changing a file's owner requires root (CAP_CHOWN); chown also fails when the user does not exist in the passwd database. This is a privilege/lookup failure, not an argument problem.

Source

Thrown at framework/helpers/BaseFileHelper.php:1017

            }
        }

        if ($mode !== null) {
            if (!is_int($mode)) {
                throw new InvalidArgumentException('$mode must be an integer or null.');
            }
            if (!chmod($path, $mode)) {
                throw new Exception('Unable to change mode of "' . $path . '" to "0' . decoct($mode) . '".');
            }
        }
        if ($user !== null && $user !== '') {
            if (is_numeric($user)) {
                $user = (int) $user;
            } elseif (!is_string($user)) {
                throw new InvalidArgumentException('The user part of $ownership must be an integer, string, or null.');
            }
            if (!chown($path, $user)) {
                throw new Exception('Unable to change user ownership of "' . $path . '" to "' . $user . '".');
            }
        }
        if ($group !== null && $group !== '') {
            if (is_numeric($group)) {
                $group = (int) $group;
            } elseif (!is_string($group)) {
                throw new InvalidArgumentException('The group part of $ownership must be an integer, string or null.');
            }
            if (!chgrp($path, $group)) {
                throw new Exception('Unable to change group ownership of "' . $path . '" to "' . $group . '".');
            }
        }
    }
}

View on GitHub (pinned to 66f00d18a2)

Solutions

  1. Run the ownership step as root in the deploy/entrypoint script rather than in application code.
  2. Verify the user exists first: posix_getpwnam($user) !== false (or check /etc/passwd) before calling.
  3. Use consistent numeric uid/gid mappings between host and containers instead of names.
  4. Catch the Exception and log path + user so failures surface in monitoring.

Example fix

// before
\yii\helpers\FileHelper::changeOwnership($upload, 'appuser');

// after
$user = \posix_getpwnam('appuser');
if ($user === false) {
    throw new \RuntimeException("User 'appuser' not found on this system");
}
\yii\helpers\FileHelper::changeOwnership($upload, $user['uid']);
Defensive patterns

Strategy: validation

Validate before calling

// Only root can chown arbitrarily; verify the target user exists
if (\function_exists('posix_getuid') && \posix_getuid() !== 0) {
    // allowed only if process already owns the file — check first
    $stat = stat($path);
    if ($stat === false || $stat['uid'] !== \posix_getuid()) {
        throw new \RuntimeException('chown requires root or file ownership');
    }
}
if (\posix_getpwnam($user) === false) {
    throw new \RuntimeException("Unknown user: {$user}");
}
\yii\helpers\FileHelper::changeOwnership($path, $user);

Try / catch

try {
    \yii\helpers\FileHelper::changeOwnership($path, $user);
} catch (\yii\base\Exception $e) {
    \Yii::error("chown failed (privileges or unknown user): {$e->getMessage()}", 'files');
    throw $e;
}

Prevention

When it happens

Trigger: A non-root process chown-ing a file to another user; a uid or name absent from /etc/passwd (typical host-vs-container uid mismatches); a container dropping CAP_CHOWN; a read-only filesystem; a typo'd username string.

Common situations: Web requests trying to re-own uploaded files to a service user; containerized apps using host usernames that exist only outside the container; deploy scripts assuming root but running as an unprivileged CI user; LDAP/NSS hiccups where user lookups intermittently fail.

Related errors


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