spatie/laravel-permission · error · TypeError
Unsupported type for $roles parameter to hasRole().
Error message
Unsupported type for $roles parameter to hasRole().
What it means
hasRole() only accepts a role name string (optionally pipe-separated like 'writer|editor'), a role primary key int/UID, a Role model instance, an array of those, an Illuminate Collection, or a BackedEnum case. Anything else falls through the type dispatch chain and hits this explicit TypeError. It exists because the method parameter is untyped in PHP, so the library enforces its documented union (string|int|array|Role|Collection|BackedEnum) manually at the fall-through point.
Source
Thrown at src/Traits/HasRoles.php:397
if ($roles instanceof Role) {
return $this->roles->contains($roles->getKeyName(), $roles->getKey());
}
if (is_array($roles)) {
foreach ($roles as $role) {
if ($this->hasRole($role, $guard)) {
return true;
}
}
return false;
}
if ($roles instanceof Collection) {
return $roles->intersect($guard ? $this->roles->where('guard_name', $guard) : $this->roles)->isNotEmpty();
}
throw new TypeError('Unsupported type for $roles parameter to hasRole().');
}
/**
* Determine if the model has any of the given role(s).
*
* Alias to hasRole() but without Guard controls
*
* @param string|int|array|Role|Collection|BackedEnum $roles
*/
public function hasAnyRole(...$roles): bool
{
return $this->hasRole($roles);
}
/**
* Determine if the model has all of the given role(s).
*
* @param string|array|Role|Collection|BackedEnum $rolesView on GitHub (pinned to afd24018f6)
Solutions
- Pass one of the supported types: a role-name string, a role id int, a Role model, an array/Collection of those, or a backed-enum case
- If the value can be null, guard before calling: if ($role) { return $user->hasRole($role); } return false;
- If you use enums for roles, back them with string or int (enum RoleEnum: string { case Admin = 'admin'; }) so the BackedEnum branch applies
- If you hold a model that is not a Role, pass its attributes instead: $user->hasRole($permission->name) is wrong, use the role's name or primary key
- Cast external/scalar-ish input explicitly: $user->hasRole((string) $request->input('role'))
Example fix
// before
$roleName = $request->input('role'); // null when the param is omitted
if ($user->hasRole($roleName)) { // TypeError: Unsupported type for $roles parameter to hasRole().
// ...
}
// after
$roleName = $request->input('role');
if ($roleName && $user->hasRole((string) $roleName)) {
// ...
} Defensive patterns
Strategy: type-guard
Validate before calling
// before calling hasRole()
if ($roles === null) {
return false; // no role to check
}
if ($roles instanceof \UnitEnum && ! $roles instanceof \BackedEnum) {
$roles = $roles->name; // unit enums are not supported; fall back to the case name
}
return $user->hasRole($roles, $guard); Type guard
/** Matches the union hasRole() accepts: string|int|array|Role|Collection|BackedEnum */
function hasRoleAccepts(mixed $roles): bool
{
return is_string($roles)
|| is_int($roles)
|| $roles instanceof \Spatie\Permission\Models\Role
|| $roles instanceof \BackedEnum
|| (is_array($roles) && array_all($roles, hasRoleAccepts(...)))
|| $roles instanceof \Illuminate\Support\Collection;
} Try / catch
try {
return $user->hasRole($roles, $guard);
} catch (\TypeError $e) {
if (str_contains($e->getMessage(), 'Unsupported type for $roles parameter')) {
\Log::warning('hasRole() called with unsupported type', ['type' => get_debug_type($roles)]);
return false;
}
throw $e; // not our TypeError — rethrow
} Prevention
- Normalize external input (request params, JSON payloads) to string or int before calling hasRole()
- Declare role enums with string or int backing so the BackedEnum branch applies
- Add the documented @param string|int|array|Role|Collection|BackedEnum $roles docblock on wrappers and run PHPStan/Psalm in CI to catch bad types early
- Short-circuit nullable lookups (if (! $role) return false;) instead of passing null through
When it happens
Trigger: Calling $user->hasRole($roles) where $roles is: null (e.g. a lookup like Role::where(...)->first() that returned null, or absent request input); a unit enum case (enum RoleEnum { case Admin; } declared without string/int backing, so it fails the BackedEnum check); a Permission model or any other non-Role object; a float or boolean; a stdClass decoded from a JSON payload.
Common situations: Passing $request->input('role') straight through when the parameter is optional and absent; using plain (non-backed) enums on PHP >= 8.1 while assuming enum support works; passing a model of a different class (Permission instead of Role); passing unserialized/JSON-decoded role objects instead of their name or id.
Understand the failure class
Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.
Related errors
- Role `{$role->name}` already exists on the global team; argu
- Please enable the teams setting in your configuration.
- Teams feature disabled, argument --team-id has no effect. Ei
- Teams feature disabled, argument --team-id has no effect. Ei
- Setup teams migration already exists. Following file was fou
AI-assisted analysis of spatie/laravel-permission@afd24018f6 (2026-08-21).
Data as JSON: /api/errors/a9fea8b7c25cf3e8.
Report an issue: GitHub.