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  $roles

View on GitHub (pinned to afd24018f6)

Solutions

  1. 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
  2. If the value can be null, guard before calling: if ($role) { return $user->hasRole($role); } return false;
  3. If you use enums for roles, back them with string or int (enum RoleEnum: string { case Admin = 'admin'; }) so the BackedEnum branch applies
  4. 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
  5. 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

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


AI-assisted analysis of spatie/laravel-permission@afd24018f6 (2026-08-21). Data as JSON: /api/errors/a9fea8b7c25cf3e8. Report an issue: GitHub.