yiisoft/yii2 · error · yii\base\InvalidArgumentException
$mode must be an integer or null.
Error message
$mode must be an integer or null.
What it means
changeOwnership() passes $mode straight to chmod(), which requires a PHP integer; strings like '0644' — the shell habit — fail is_int() and throw InvalidArgumentException before any filesystem call. PHP never auto-converts octal strings, and the octal-looking text is a string, so the API rejects it up front.
Source
Thrown at framework/helpers/BaseFileHelper.php:1004
$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);
$user = ArrayHelper::getValue($ownership, $ownershipIsIndexed ? 0 : 'user');
$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;View on GitHub (pinned to 66f00d18a2)
Solutions
- Use an octal integer literal: 0644, 0755 — never the quoted string form.
- Cast config strings with base 8: $mode = intval($config['mode'], 8);
- Validate before calling: is_int($mode), or is_string($mode) && ctype_digit($mode) followed by an intval(..., 8) cast.
Example fix
// before
\yii\helpers\FileHelper::changeOwnership($file, 'www-data', '0755');
// after
$mode = intval('0755', 8); // int 493
\yii\helpers\FileHelper::changeOwnership($file, 'www-data', $mode); Defensive patterns
Strategy: type-guard
Validate before calling
if (is_string($mode)) {
$mode = intval($mode, 8); // '0644' → 420
}
if (!is_int($mode)) {
throw new \InvalidArgumentException('Mode must be an octal int, e.g. 0644');
}
\yii\helpers\FileHelper::changeOwnership($path, 'www-data', $mode); Type guard
/** @param mixed $mode */
function isModeValue($mode): bool
{
return $mode === null || is_int($mode) || (is_string($mode) && ctype_digit($mode));
} Prevention
- Use octal integer literals (0644) in PHP, never quoted strings.
- Cast modes from config/DB with intval($value, 8).
- Remember JSON stores numbers as strings when quoted — validate config schemas for numeric modes.
When it happens
Trigger: FileHelper::changeOwnership($path, 'www-data', '0644'); a mode loaded from JSON/DB config, where values always arrive as strings; a mode computed as '0' . decoct($m) and not cast back; passing mode from a framework-agnostic config array shared with shell scripts.
Common situations: Config-driven permission settings where YAML parses 0644 as int but JSON keeps '0644' a string; porting shell snippets (chmod 0644 ...) to PHP; modes stored in environment variables.
Related errors
- Unable to change mode of "{$path}" to "0{$mode}".
- Failed to change permissions for directory "{$path}": {messa
- Unable to open directory: $dir
- Exclude/include pattern must be a string.
- $ownership must be an integer, string, array, or null.
AI-assisted analysis of yiisoft/yii2@66f00d18a2 (2026-08-17).
Data as JSON: /api/errors/455cf98b5ea0687b.
Report an issue: GitHub.