Skip to content
RustPluginGenerator
Runtime errorOxide + Carbon

Fix "Failed to run a 5.00 timer in 'AutoDoors'" NullReferenceException (Oxide)

A timer callback threw a NullReferenceException, so Oxide logged the failure and destroyed that timer. The usual cause is a stored door or entity that was destroyed before the delayed callback ran; re-check the reference for null and IsDestroyed inside the callback.

Server console
Failed to run a 5.00 timer in 'AutoDoors v1.0.0' (NullReferenceException: Object reference not set to an instance of an object)
  at Oxide.Plugins.AutoDoors+<>c__DisplayClass6_0.<OnDoorOpened>b__0 () [0x00001] in <8f3c1a2b4d5e4f6a9b0c1d2e3f4a5b6c>:0

Why it happens

timer.Once schedules a delegate that runs later, on a later frame or seconds after the trigger. Between scheduling and firing, the door can be removed by a raid, a server wipe, a despawn, or a plugin that calls Kill(). The delegate still holds the old reference, and Oxide does not null it for you. When the callback dereferences that destroyed entity (for example door.IsOpen(), door.transform.position or door.net.ID), it throws NullReferenceException. Oxide catches the exception, prints "Failed to run a 5.00 timer in '...'" with the stack trace, and destroys only that timer. The plugin stays loaded and other hooks keep running. The <>c__DisplayClass6_0.<OnDoorOpened>b__0 frame in the trace is a lambda created inside OnDoorOpened, which tells you the reference captured there is what to guard. Entity references can also be null if the entity never spawned.

How to fix it

  1. Open the hook named in the stack trace (OnDoorOpened in the example) and find the timer.Once call. 2. Inside the lambda, before using the captured reference, add if (door == null || door.IsDestroyed) return;. For non-door entities use entity == null || entity.IsDestroyed (or net == null). 3. Null-check nested members too (door.net, player.net.connection, GetOwnerPlayer()). 4. If the callback reads a player, check player == null || !player.IsConnected first, since players can disconnect during the delay. 5. Save the file; Oxide reloads it automatically. Confirm with oxide.reload AutoDoors, open a door, and watch the console. A clean run prints the usual Loaded plugin ... line and no Failed to run a ... timer line. To reproduce the old bug on purpose, destroy a scheduled door before the timer fires; with the fix the callback returns silently.

Before and after

BeforeFails
using System;
using Oxide.Core;
using UnityEngine;
using Oxide.Core.Plugins;

namespace Oxide.Plugins
{
    [Info("AutoDoors", "RustPluginGenerator", "1.0.0")]
    [Description("Closes doors automatically five seconds after a player opens them.")]
    public class AutoDoors : RustPlugin
    {
        private void OnDoorOpened(Door door, BasePlayer player)
        {
            if (door == null || player == null) return;
            if (!door.IsOpen()) return;

            // The reference to the door is captured here. If the door is
            // destroyed before this fires, the callback throws a
            // NullReferenceException and Oxide logs:
            // Failed to run a 5.00 timer in 'AutoDoors v1.0.0' (...)
            timer.Once(5f, () =>
            {
                door.SetFlag(BaseEntity.Flags.Open, false);
                door.SendNetworkUpdateImmediate();
            });
        }
    }
}
AfterFixed
using System;
using Oxide.Core;
using UnityEngine;
using Oxide.Core.Plugins;

namespace Oxide.Plugins
{
    [Info("AutoDoors", "RustPluginGenerator", "1.0.0")]
    [Description("Closes doors automatically five seconds after a player opens them.")]
    public class AutoDoors : RustPlugin
    {
        private void OnDoorOpened(Door door, BasePlayer player)
        {
            if (door == null || door.IsDestroyed) return;
            if (!door.IsOpen()) return;

            timer.Once(5f, () =>
            {
                // The door may have been destroyed, despawned or wiped
                // while the timer was waiting. Never touch a dead entity.
                if (door == null || door.IsDestroyed) return;

                // Extra guard in case another plugin already closed it.
                if (!door.IsOpen()) return;

                door.SetOpen(false);
                door.SendNetworkUpdateImmediate();
            });
        }
    }
}

FAQ

Does this error unload my plugin?

No. Oxide destroys only the timer that threw, prints the exception and stack trace, and keeps the plugin loaded and running. Other timers created elsewhere are unaffected. Fix the callback and the error stops appearing on the next reload.

Why does the trace mention a DisplayClass and b__0?

That is compiler-generated code for a lambda. <>c__DisplayClass6_0 is the closure that captured your local variables (like the door) and b__0 is the lambda body passed to timer.Once. It confirms the exception happened inside the timer callback, not elsewhere in the hook.

Is null-checking enough, or should I also destroy timers in Unload?

Both. Null and IsDestroyed checks keep the callback safe. In Unload call Destroy() on stored Timer references so nothing runs after the plugin is unloaded. A timer whose plugin has gone away still needs the guard if it holds an entity reference.

How do I know which timer is failing when several are scheduled?

The message includes the delay, for example "a 5.00 timer". Match it to your timer.Once(5f, ...) calls and to the hook name in the stack trace. If several use the same delay, add a log line temporarily or split the delays to isolate the one throwing.

Different error?

Paste the full console output and get the cause and a fix in plain English.

Open the error explainer

Skip the debugging

Generated plugins are compiled before you get them, and compiler errors are fixed automatically.

Generate a plugin

2 free plugins a month, no card.

Last updated Oct 26, 2026.

Get plugins that compile first time

Generate