{"record":{"id":"a9fea8b7c25cf3e8","repo":"spatie/laravel-permission","slug":"unsupported-type-for-roles-parameter-to-hasrole","errorCode":null,"errorMessage":"Unsupported type for $roles parameter to hasRole().","messagePattern":"Unsupported type for \\$roles parameter to hasRole\\(\\)\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/Traits/HasRoles.php","lineNumber":397,"sourceCode":"        if ($roles instanceof Role) {\n            return $this->roles->contains($roles->getKeyName(), $roles->getKey());\n        }\n\n        if (is_array($roles)) {\n            foreach ($roles as $role) {\n                if ($this->hasRole($role, $guard)) {\n                    return true;\n                }\n            }\n\n            return false;\n        }\n\n        if ($roles instanceof Collection) {\n            return $roles->intersect($guard ? $this->roles->where('guard_name', $guard) : $this->roles)->isNotEmpty();\n        }\n\n        throw new TypeError('Unsupported type for $roles parameter to hasRole().');\n    }\n\n    /**\n     * Determine if the model has any of the given role(s).\n     *\n     * Alias to hasRole() but without Guard controls\n     *\n     * @param  string|int|array|Role|Collection|BackedEnum  $roles\n     */\n    public function hasAnyRole(...$roles): bool\n    {\n        return $this->hasRole($roles);\n    }\n\n    /**\n     * Determine if the model has all of the given role(s).\n     *\n     * @param  string|array|Role|Collection|BackedEnum  $roles","sourceCodeStart":379,"sourceCodeEnd":415,"githubUrl":"https://github.com/spatie/laravel-permission/blob/afd24018f68306e8b43ace487c107a59af1be776/src/Traits/HasRoles.php#L379-L415","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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'))"],"exampleFix":"// before\n$roleName = $request->input('role'); // null when the param is omitted\nif ($user->hasRole($roleName)) { // TypeError: Unsupported type for $roles parameter to hasRole().\n    // ...\n}\n\n// after\n$roleName = $request->input('role');\nif ($roleName && $user->hasRole((string) $roleName)) {\n    // ...\n}","handlingStrategy":"type-guard","validationCode":"// before calling hasRole()\nif ($roles === null) {\n    return false; // no role to check\n}\nif ($roles instanceof \\UnitEnum && ! $roles instanceof \\BackedEnum) {\n    $roles = $roles->name; // unit enums are not supported; fall back to the case name\n}\nreturn $user->hasRole($roles, $guard);","typeGuard":"/** Matches the union hasRole() accepts: string|int|array|Role|Collection|BackedEnum */\nfunction hasRoleAccepts(mixed $roles): bool\n{\n    return is_string($roles)\n        || is_int($roles)\n        || $roles instanceof \\Spatie\\Permission\\Models\\Role\n        || $roles instanceof \\BackedEnum\n        || (is_array($roles) && array_all($roles, hasRoleAccepts(...)))\n        || $roles instanceof \\Illuminate\\Support\\Collection;\n}","tryCatchPattern":"try {\n    return $user->hasRole($roles, $guard);\n} catch (\\TypeError $e) {\n    if (str_contains($e->getMessage(), 'Unsupported type for $roles parameter')) {\n        \\Log::warning('hasRole() called with unsupported type', ['type' => get_debug_type($roles)]);\n\n        return false;\n    }\n\n    throw $e; // not our TypeError — rethrow\n}","preventionTips":["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"],"tags":["php","laravel","spatie-permission","typeerror","roles","authorization"],"backgroundTag":"invalid-argument-type","analyzedSha":"afd24018f68306e8b43ace487c107a59af1be776","analyzedAt":"2026-08-21T01:13:42.706Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}