OnUserGroupRemoved
OnUserGroupRemoved is called by Oxide's permission system after a group has been removed from a user's permission record. It fires only when the removal actually succeeded, not when the user did not belong to that group.
void OnUserGroupRemoved(string playerId, string groupName)Parameters
| Parameter | Type | Description |
|---|---|---|
playerId | string | The Steam ID of the user who lost the group, as a string (same value as BasePlayer.UserIDString). It is the identifier Oxide's permission system uses for that user's group membership record. |
groupName | string | The name of the Oxide permission group that was removed from the user. This is the raw group key registered with permission.RegisterGroup, not a display title. |
Return value
The hook's return value is ignored. Permission.RemoveUserGroup continues its normal code path regardless of what you return, so returning a value has no effect on the removal.
How it works
This hook fires from Permission.RemoveUserGroup, the method Oxide calls whenever a group is taken off a user — from the oxide.usergroup remove console command, from another plugin calling permission.RemoveUserGroup(...), or from any admin flow that strips membership. The call happens after userData.Groups.Remove(groupName) returns true, so a removal attempt for a group the user never had returns early and produces no hook call.
playerId is the user's Steam ID string; it can belong to an offline player, so do not assume a BasePlayer exists. groupName is the permission group key. Because the hook runs after Oxide has already updated its permission data, re-granting inside the hook works but will trigger OnUserGroupAdded in turn.
There is no more specific parameter type to declare: both arguments are plain strings. You cannot narrow the hook by player type, so filter manually on playerId if you only care about a subset of players or groups. The hook is a notification only; it cannot cancel the removal.
Example plugin: Strip VIP permission on group removal and notify the player
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("Group Removal Vip Sync", "RustPluginGenerator", "1.0.0")]
[Description("Revokes a permission from users who are removed from a watched group and notifies them in chat")]
public class GroupRemovalVipSync : RustPlugin
{
private const string PermNotify = "groupremovalvipsync.notify";
private PluginConfig config;
private class PluginConfig
{
[JsonProperty("Watched group name")]
public string WatchedGroup = "vip";
[JsonProperty("Permission to revoke on removal")]
public string RevokePermission = "groupremovalvipsync.vip";
}
protected override void LoadDefaultConfig() => config = new PluginConfig();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
config = Config.ReadObject<PluginConfig>();
if (config == null) throw new Exception();
}
catch
{
PrintWarning("Configuration file is invalid, using defaults");
LoadDefaultConfig();
}
SaveConfig();
}
protected override void SaveConfig() => Config.WriteObject(config);
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
["Removed"] = "You were removed from the {0} group and lost its perks."
}, this);
}
private string Msg(string key, string userId = null, params object[] args) =>
string.Format(lang.GetMessage(key, this, userId), args);
private void Init()
{
permission.RegisterPermission(PermNotify, this);
permission.RegisterPermission(config.RevokePermission, this);
}
private void OnUserGroupRemoved(string playerId, string groupName)
{
if (string.IsNullOrEmpty(playerId) || string.IsNullOrEmpty(groupName)) return;
if (!string.Equals(groupName, config.WatchedGroup, StringComparison.OrdinalIgnoreCase)) return;
if (!string.IsNullOrEmpty(config.RevokePermission) &&
permission.UserHasPermission(playerId, config.RevokePermission))
{
permission.RevokeUserPermission(playerId, config.RevokePermission);
}
var player = BasePlayer.FindByID(ulong.Parse(playerId));
if (player == null || !player.IsConnected) return;
if (permission.UserHasPermission(playerId, PermNotify))
player.ChatMessage(Msg("Removed", playerId, groupName));
}
}
}The plugin watches a configured VIP group name. When a user is removed from that group, it revokes the matching permission node from the user so leftover grants do not survive the membership change, and it tells the player in chat if they are online. All group and permission names come from the config so the same file works on servers with different naming. The hook itself stays silent — there is no console output — and only sends a single localized chat message, keeping it cheap even when bulk permission edits run during a wipe. LoadDefaultConfig and the guarded LoadConfig keep the plugin usable if the config file is edited badly.
Common use cases
Sync donor permissions to membership
When a user leaves a VIP or donor group, revoke the permission flags that group was supposed to carry, so removing the group in the admin UI is enough to fully downgrade the player.
Audit trail for admin actions
Record who lost which group and when in an oxide/data file, then let admins review recent membership changes without digging through server console logs.
Reset plugin-specific state
If a plugin grants kits, teleport quotas or homes based on a group, clear that stored state when the group is removed so the player does not keep perks tied to a group they no longer have.
Pitfalls
The player may be offline
playerId is only a string identifier. Use BasePlayer.FindByID or FindAwakeOrSleeping and null-check the result before sending chat or touching the player object.
Not called for users who lacked the group
Oxide returns early when the user is not a member of the group, so you get no hook call for a no-op removal. Do not rely on this hook as proof that a command was executed.
Beware re-entrant permission changes
Calling permission.RevokeUserPermission or permission.AddUserGroup inside the hook mutates the same user data and can cascade into OnUserPermissionRevoked or OnUserGroupAdded handlers. Keep the work inside the hook small and avoid loops.
Build a plugin with OnUserGroupRemoved
Describe what should happen and get a compiled plugin that uses this hook.
Edit the prompt on the next screen. 2 free plugins a month, no card.
Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.
Build a plugin with OnUserGroupRemoved
Generate