z-song/laravel-admin · warning
$slug is existed
Error message
$slug is existed
What it means
This is not an exception but a console warning emitted by laravel-admin's `admin:permissions` command (src/Console/PermissionCommand.php:76). The command scans every database table (minus the admin system tables) and auto-generates seven CRUD permission rows per table (list, view, create, edit, delete, export, filter) with slugs like `users.list`. When `Permission::where('slug', $slug)->exists()` is true, the row is skipped and the command prints "<slug> is existed" so you know that permission was not regenerated. It is an idempotency notice: the command never overwrites existing permissions.
Source
Thrown at src/Console/PermissionCommand.php:76
$permissions = $this->getPermissions();
foreach ($tables as $table) {
foreach ($permissions as $permission => $permission_lang) {
$http_method = $this->generateHttpMethod($permission);
$http_path = $this->generateHttpPath($table, $permission);
$slug = $this->generateSlug($table, $permission);
$name = $this->generateName($table, $permission_lang);
$exists = Permission::where('slug', $slug)->exists();
if (!$exists) {
Permission::create([
'name' => $name,
'slug' => $slug,
'http_method' => $http_method,
'http_path' => $http_path,
]);
$this->info("$slug is generated");
} else {
$this->warn("$slug is existed");
}
}
}
}
private function getAllTables()
{
return array_map('current', DB::select('SHOW TABLES'));
}
private function getIgnoreTables()
{
return [
config('admin.database.users_table'),
config('admin.database.roles_table'),
config('admin.database.permissions_table'),
config('admin.database.menu_table'),
config('admin.database.operation_log_table'),View on GitHub (pinned to 67c441eb78)
Solutions
- Treat the message as informational — nothing is broken; the permission already exists and was intentionally skipped.
- If you want the command to regenerate a permission, delete or rename the existing row first (in the admin panel under Authority > Permission, or `DB::table(config('admin.database.permissions_table'))->where('slug', $slug)->delete()`), then re-run `php artisan admin:permissions`.
- To regenerate everything, truncate the permissions + role_permissions + user_permissions pivot tables and re-run the command (only in a safe environment; role assignments will be lost).
- Scope the run to new tables only: `php artisan admin:permissions --tables=new_table1,new_table2` to avoid warnings for tables already processed.
- If two tables map to the same slug, rename one table or manually edit the colliding permission so each resource has distinct slugs.
Example fix
// before: re-running generates warnings and never updates existing permissions
php artisan admin:permissions // "users.list is existed", values unchanged
// after: remove the stale row, then regenerate so http_path/http_method are rebuilt
php artisan tinker --execute="\Encore\Admin\Auth\Database\Permission::where('slug','users.list')->delete();"
php artisan admin:permissions --tables=users // "users.list is generated" Defensive patterns
Strategy: validation
Validate before calling
// Before running admin:permissions, see which slugs already exist (these will be skipped):
$existing = \Encore\Admin\Auth\Database\Permission::whereIn('slug', $slugs)->pluck('slug');
// e.g. check one table's set:
$table = 'users';
$slugs = collect(['list','view','create','edit','delete','export','filter'])
->map(fn ($action) => \Illuminate\Support\Str::kebab(\Illuminate\Support\Str::camel($table)).'.'.$action);
$duplicates = \Encore\Admin\Auth\Database\Permission::whereIn('slug', $slugs)->pluck('slug');
if ($duplicates->isNotEmpty()) {
echo "Will be skipped (already exist): ".$duplicates->implode(', ').PHP_EOL;
} Type guard
// PHP has no type guard here (no exception thrown); treat warn output as a signal.
// After running, assert the permission set you expect exists:
function permissionExists(string $slug): bool
{
return \Encore\Admin\Auth\Database\Permission::where('slug', $slug)->exists();
} Prevention
- Run `admin:permissions` only for newly created tables: pass `--tables=foo,bar` instead of letting it scan the whole database.
- Remember the command is skip-only: to change an existing permission's http_path or http_method, edit the row in the admin panel or delete it and re-run.
- Do not manually create permissions whose slugs match the generated pattern `kebab-table.action` unless you intend the command to skip them.
- Beware slug collisions: `Str::kebab(Str::camel($table))` merges differently named tables (user_roles vs userRoles) into one slug.
- Automate checks in scripts: query the permissions table for expected slugs instead of parsing 'is existed' output.
When it happens
Trigger: Running `php artisan admin:permissions` a second time after a successful first run (every slug now exists). Running it after `admin:install` already seeded permissions, or after you created matching permissions manually in the admin panel (slug `resource.action`). Two different table names that collapse to the same kebab slug (e.g. `user_roles` and `userRoles` both produce `user-roles.list`). Specifying `--tables=` with tables whose permissions were already generated.
Common situations: Re-running the command hoping to refresh permissions after adding new columns or renaming tables (it will not update existing rows — only skip them). Environments where permissions were customized via the admin UI and the developer expects the command to overwrite them. Multi-developer teams where one member seeded permissions and another re-runs the generator. Copying a database between environments (permissions already present) and then running the generator.
AI-assisted analysis of z-song/laravel-admin@67c441eb78 (2026-08-21).
Data as JSON: /api/errors/9834bc9c3d62a4db.
Report an issue: GitHub.