{"record":{"id":"beebb46090c16141","repo":"spatie/laravel-permission","slug":"role-role-name-already-exists-on-the-global","errorCode":null,"errorMessage":"Role `{$role->name}` already exists on the global team; argument --team-id has no effect","messagePattern":"Role `(.+?)` already exists on the global team; argument --team-id has no effect","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/Commands/CreateRoleCommand.php","lineNumber":39,"sourceCode":"    public function handle(PermissionRegistrar $permissionRegistrar): int\n    {\n        $roleClass = app(RoleContract::class);\n\n        $teamIdAux = getPermissionsTeamId();\n        setPermissionsTeamId($this->option('team-id') ?: null);\n\n        if (! $permissionRegistrar->teams && $this->option('team-id')) {\n            $this->warn('Teams feature disabled, argument --team-id has no effect. Either enable it in permissions config file or remove --team-id parameter');\n\n            return self::SUCCESS;\n        }\n\n        $role = $roleClass::findOrCreate($this->argument('name'), $this->argument('guard'));\n        setPermissionsTeamId($teamIdAux);\n\n        $teams_key = $permissionRegistrar->teamsKey;\n        if ($permissionRegistrar->teams && $this->option('team-id') && is_null($role->$teams_key)) {\n            $this->warn(\"Role `{$role->name}` already exists on the global team; argument --team-id has no effect\");\n        }\n\n        $role->givePermissionTo($this->makePermissions($this->argument('permissions')));\n\n        $this->info(\"Role `{$role->name}` \".($role->wasRecentlyCreated ? 'created' : 'updated'));\n\n        return self::SUCCESS;\n    }\n\n    protected function makePermissions(?string $string = null): ?Collection\n    {\n        if (empty($string)) {\n            return null;\n        }\n\n        $permissionClass = app(PermissionContract::class);\n\n        $permissions = explode('|', $string);","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/spatie/laravel-permission/blob/afd24018f68306e8b43ace487c107a59af1be776/src/Commands/CreateRoleCommand.php#L21-L57","documentation":"With teams enabled, permission:create-role --team-id=X looks the role up via Role::findOrCreate(). The underlying query matches roles where the teams column IS NULL (global role) OR equals the current team id, so an existing global role with the same name and guard is found and returned instead of a team-scoped one being created. When the returned role's team key is null, the command warns that the --team-id you passed had no effect, and proceeds to attach the listed permissions to that pre-existing global role.","triggerScenarios":"Running `php artisan permission:create-role writer web \"edit posts\" --team-id=2` when a role named 'writer' for guard 'web' already exists with team_id NULL. findOrCreate() returns the global role, the `$permissionRegistrar->teams && $this->option('team-id') && is_null($role->$teams_key)` condition is true, and the warning fires.","commonSituations":"Migrating a single-tenant app to teams: all pre-existing roles are global (team_id NULL), so the first team-scoped create for an existing name updates the global role instead; two teams that should each have their own 'admin' role collide with the global 'admin'; seeders that create global roles first and later run team-scoped create commands.","solutions":["Delete or rename the existing global role first (e.g. in tinker: Spatie\\Permission\\Models\\Role::where('name','writer')->whereNull('team_id')->first()?->delete()), then re-run the command with --team-id","Create the team-scoped role explicitly with the team id set: in tinker, setPermissionsTeamId(2); Role::findOrCreate('writer', 'web') — or Role::create(['name' => 'writer', 'guard_name' => 'web', 'team_id' => 2])","If a global role with that name is actually acceptable, drop --team-id and let the command update the global role deliberately","Adopt per-team role naming (e.g. 'writer-team-2') if your app logic cannot tolerate the global-first lookup"],"exampleFix":"# before — a global 'writer' role exists (team_id NULL)\nphp artisan permission:create-role writer web \"edit posts\" --team-id=2\n# warning: Role `writer` already exists on the global team; argument --team-id has no effect\n\n# after — remove the global role, then create the team-scoped one\nphp artisan tinker --execute=\"\\Spatie\\Permission\\Models\\Role::where('name','writer')->whereNull('team_id')->first()?->delete();\"\nphp artisan permission:create-role writer web \"edit posts\" --team-id=2","handlingStrategy":"validation","validationCode":"// before creating a team-scoped role, make sure no global role shadows it\n$registrar = app(\\Spatie\\Permission\\PermissionRegistrar::class);\n$globalExists = \\Spatie\\Permission\\Models\\Role::where('name', 'writer')\n    ->where('guard_name', 'web')\n    ->whereNull($registrar->teamsKey)\n    ->exists();\nif ($globalExists) {\n    // rename/delete the global role, or pick a per-team name, before running with --team-id\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Remember the lookup order: a global role (team column NULL) matches before a team-scoped one with the same name and guard","When adopting teams, decide per existing global role whether to scope it (set team_id) or keep it global — do not leave accidental globals around","In seeders, create team-scoped roles with setPermissionsTeamId($teamId) in effect and assert $role->team_id === $teamId afterwards"],"tags":["laravel","artisan","spatie-permission","teams","multi-tenant","roles"],"backgroundTag":"duplicate-record-found","analyzedSha":"afd24018f68306e8b43ace487c107a59af1be776","analyzedAt":"2026-08-21T01:13:42.706Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}