Fix "Duplicate permission registered 'kits.use'" in Oxide plugins
The warning means permission.RegisterPermission was called more than once for the same permission string during a single load of the plugin. It is a warning, not a crash, but it signals that registration runs twice — usually from both Init and Loaded, or once per config entry. Register each permission exactly once in Init.
Duplicate permission registered 'kits.use' (by plugin 'Kits')
Why it happens
Oxide keeps a global permission registry. Each plugin should register its permissions once, in Init, so they exist before any command or hook checks permission.UserHasPermission. When RegisterPermission is called again for a name that already exists, Oxide logs Duplicate permission registered '<perm>' (by plugin '<Title>').
Common triggers in Rust plugins:
- Registration placed in both
InitandLoaded, so a single load registers twice. - A loop that registers a permission per kit, shop item or config entry, where two entries produce the same string (e.g.
kits.userepeated). - The same permission string registered under two constant name variants that differ only in case or whitespace.
- A helper called from multiple hooks (e.g.
OnServerInitializedand a chat command) that registers lazily on first use.
The plugin still loads and the permission works; the message is Oxide telling you the registry was written twice.
How to fix it
- Open the plugin and find every
permission.RegisterPermissioncall. Use search in your editor; there should normally be one block of calls. - Keep all registrations in
private void Init()and delete any copy fromLoaded,OnServerInitialized, chat commands or other hooks. - If you register inside a loop over config entries, guard against duplicate names: check a
HashSet<string>before registering, or build the permission from the class-name prefix plus a sanitised entry name. - Use constants for the permission strings so two call sites cannot drift apart.
- Save the file and run
oxide.reload Kits. The console should printLoaded plugin Kits v1.0.0 by RustPluginGeneratorand no duplicate warning. If the warning returns, grep the source for the permission name to confirm only one call site remains.
Before and after
using System.Collections.Generic;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Kits", "RustPluginGenerator", "1.0.0")]
[Description("Gives a starter kit through a chat command")]
public class Kits : RustPlugin
{
private const string PermStarter = "kits.use";
private readonly Dictionary<string, string> kits = new Dictionary<string, string>
{
["starter"] = "rock",
["starter"] = "torch"
};
private void Init()
{
// Registers "kits.use" for every kit, including the duplicated key.
foreach (var kit in kits)
{
permission.RegisterPermission(PermStarter, this);
}
}
private void Loaded()
{
// Registered again on load, so the same permission hits the registry twice.
permission.RegisterPermission(PermStarter, this);
}
[ChatCommand("kit")]
private void CmdKit(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!permission.UserHasPermission(player.UserIDString, PermStarter))
{
SendReply(player, "You do not have permission to use kits.");
return;
}
var item = ItemManager.CreateByName("rock", 1, 0);
if (item == null) return;
player.GiveItem(item);
SendReply(player, "Starter kit given.");
}
}
}using System.Collections.Generic;
using Oxide.Core;
using UnityEngine;
namespace Oxide.Plugins
{
[Info("Kits", "RustPluginGenerator", "1.0.0")]
[Description("Gives a starter kit through a chat command")]
public class Kits : RustPlugin
{
private const string PermStarter = "kits.use";
private readonly Dictionary<string, string> kits = new Dictionary<string, string>
{
["starter.rock"] = "rock",
["starter.torch"] = "torch"
};
private void Init()
{
// Registered exactly once, outside any loop.
permission.RegisterPermission(PermStarter, this);
}
[ChatCommand("kit")]
private void CmdKit(BasePlayer player, string command, string[] args)
{
if (player == null) return;
if (!permission.UserHasPermission(player.UserIDString, PermStarter))
{
SendReply(player, "You do not have permission to use kits.");
return;
}
if (args == null || args.Length == 0)
{
SendReply(player, "Usage: /kit starter");
return;
}
if (!kits.ContainsKey(args[0]))
{
SendReply(player, "Unknown kit.");
return;
}
foreach (var entry in kits)
{
if (!entry.Key.StartsWith(args[0])) continue;
var item = ItemManager.CreateByName(entry.Value, 1, 0);
if (item != null) player.GiveItem(item);
}
SendReply(player, "Kit given.");
}
}
}FAQ
Does the duplicate warning break the permission?
No. The permission exists and permission.UserHasPermission returns correctly. Oxide logs the message because the registry entry was written a second time; the plugin stays loaded and keeps running. Treat it as a code smell rather than an outage.
Why does it show once per reload?
The registry entry persists across a reload, so the second registration during the new load hits an existing name and logs again. Fixing the call site stops it on the next oxide.reload. Restarting the server also clears the registry, which is why a fresh boot may hide a double registration.
Can two different plugins cause this?
Only if they share a permission string. If plugin A registers kits.use and plugin B also registers kits.use, the second one logs the warning. Keep permissions prefixed with your own plugin's lowercase class name so names cannot collide.
Is this the same as the missing-prefix warning?
No. Missing plugin name prefix means the permission string does not start with the plugin class name. Both can appear together if a prefix-less permission is registered twice; fixing the prefix and moving registration into Init clears both.
Different error?
Paste the full console output and get the cause and a fix in plain English.
Open the error explainerSkip the debugging
Generated plugins are compiled before you get them, and compiler errors are fixed automatically.
Generate a plugin2 free plugins a month, no card.
Last updated Oct 26, 2026.
Get plugins that compile first time
Generate