OnItemRemove
OnItemRemove is called when Item.Remove is invoked on an item, before the item is actually destroyed or despawned. Returning a non-null value cancels the removal.
object OnItemRemove(Item item)Parameters
| Parameter | Type | Description |
|---|---|---|
item | Item | The Item instance that is about to be removed. Inspect its info.shortname, skin, amount and GetOwnerPlayer() to decide whether removal should be blocked. Never call Remove() on it from inside the hook. |
Return value
Return null (or fall through) to let the game proceed with removal. Return any other value, including false, to stop Item.Remove completely, leaving the item intact in its container.
How it works
Oxide fires OnItemRemove inside Item.Remove(float fTime). That method runs whenever the server wants the item gone: consuming food and meds, using up ammo or resources, dropping via inventory operations, removing items from containers, despawn timers on dropped items, crafting consumption and cleanup of invalid items. The call happens first, so the item still has its container, owner, amount and condition when your hook runs. A non-null return stops the method immediately, so the item stays where it is and no despawn timer is set. Because the hook has no return-type refinement (the game method returns void), any non-null object except null blocks the removal, even false; return null unless you intend to veto. Use the item parameter to filter by shortname, skin, container, or the owning player via item.GetOwnerPlayer(). Declaring a more specific parameter type is not possible here, so filter inside the method. Avoid heavy work: this fires often, and calling item.Remove() from inside creates recursion.
Example plugin: Prevent players from removing certain items
using System.Linq;
using UnityEngine;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;
namespace Oxide.Plugins
{
[Info("No Remove Lucky Charm", "RustPluginGenerator", "1.0.0")]
[Description("Prevents players from removing configured items from their inventory.")]
public class NoRemoveLuckyCharm : RustPlugin
{
private const string PermBypass = "noremoveluckycharm.bypass";
private PluginConfig config;
private readonly Dictionary<ulong, float> warnCooldown = new Dictionary<ulong, float>();
private class PluginConfig
{
[JsonProperty("Protected item shortnames")]
public List<string> ProtectedItems = new List<string> { "note", "blueprintbase" };
[JsonProperty("Warn cooldown seconds")]
public float WarnCooldown = 5f;
}
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>
{
["Protected"] = "You cannot remove {0} from your inventory."
}, 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(PermBypass, this);
}
private object OnItemRemove(Item item)
{
if (item == null || item.info == null) return null;
if (config.ProtectedItems == null || !config.ProtectedItems.Contains(item.info.shortname)) return null;
BasePlayer owner = item.GetOwnerPlayer();
if (owner == null || !owner.IsConnected) return null;
if (permission.UserHasPermission(owner.UserIDString, PermBypass)) return null;
float now = UnityEngine.Time.realtimeSinceStartup;
float next;
if (!warnCooldown.TryGetValue(owner.userID, out next) || now >= next)
{
warnCooldown[owner.userID] = now + config.WarnCooldown;
owner.ChatMessage(Msg("Protected", owner.UserIDString, item.info.displayName.english));
}
return true;
}
}
}The plugin keeps a configurable list of shortnames that players cannot get rid of. In OnItemRemove it rejects the removal only when the item's shortname is listed and the item currently sits in a player inventory (item.GetOwnerPlayer() is not null), so admin cleanup, container despawn and server-side removal still work. Guarded items get a translated chat warning, throttled per player through a short dictionary cooldown so the message is not spammed when a player repeatedly tries to drop or consume the item. The config class carries the shortname list with sane defaults and a LoadDefaultConfig fallback so a broken config never stops the plugin from loading.
Common use cases
Protect event reward items
Block removal of skins or items handed out by an event plugin so players can't accidentally consume or drop them, while still letting the plugin itself remove them via a flag.
No-drop rule for map items
Prevent players from dropping a specific shortname by vetoing removal only when the item's owner is connected and the item is in a player inventory.
Audit before disposal
Log the removal of rare items to a data file, returning null afterwards so the removal still happens while you keep a record of who lost what.
Pitfalls
Returning false is not 'allow'
The check is for a non-null value, so
return false;blocks the removal exactly likereturn true;. Always return null when you want the item removed.Blocking too broadly
This hook covers ammo consumption, food use, crafting and despawn. Filter by
GetOwnerPlayer(), container type and shortname, or you will also stop server cleanup and map reset logic.Recursive Remove calls
Calling
item.Remove()inside the hook re-enters the patched method. Set a flag before calling and check it at the top of the hook, or call Remove outside the hook with NextTick.
Build a plugin with OnItemRemove
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 OnItemRemove
Generate