yiisoft/yii2 · error · yii\base\InvalidArgumentException

The user part of $ownership must be an integer, string, or n

Error message

The user part of $ownership must be an integer, string, or null.

What it means

Inside changeOwnership(), the extracted 'user' part must be numeric-coercible, a string, or null; an array or object (e.g. pulled out of the associative ownership array) hits the elseif !is_string branch and throws InvalidArgumentException before chown() is called. The defect is in how the ownership array/string was assembled, not in the environment.

Source

Thrown at framework/helpers/BaseFileHelper.php:1014

                $group = ArrayHelper::getValue($ownership, $ownershipIsIndexed ? 1 : 'group');
            } else {
                throw new InvalidArgumentException('$ownership must be an integer, string, array, or null.');
            }
        }

        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. Flatten config to scalar user/group values ('user' => 'www-data').
  2. Validate the extracted user before the call: is_numeric($user) || is_string($user) || $user === null.
  3. Cast numeric strings to int and pass plain strings otherwise.

Example fix

// before
\yii\helpers\FileHelper::changeOwnership($file, ['user' => ['name' => 'www-data']]);

// after
\yii\helpers\FileHelper::changeOwnership($file, ['user' => 'www-data']);
Defensive patterns

Strategy: type-guard

Validate before calling

$user = is_array($ownership) ? ($ownership['user'] ?? null) : $ownership;
if (!(is_numeric($user) || is_string($user) || $user === null)) {
    throw new \InvalidArgumentException('Ownership user part must be scalar');
}

Type guard

/** @param mixed $user */
function isUserPart($user): bool
{
    return $user === null || $user === '' || is_numeric($user) || is_string($user);
}

Prevention

When it happens

Trigger: ['user' => ['name' => 'www-data']] — a nested array under the 'user' key; ArrayHelper::getValue($ownership, 'user') returning an array default; indexed form [0 => ['uid' => 33], 1 => 33] where the user slot holds an array.

Common situations: YAML config producing nested maps for ownership; forwarding unchecked request/config data into the ownership argument; config schemas that allow both scalar and structured forms and default to the structured one.

Related errors


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