Game Dev Cheat Sheet

Runtime exceptions

MissingReferenceException: The object of type 'X' has been destroyed but you are still trying to access it.

The exact message

MissingReferenceException: The object of type 'X' has been destroyed but you are still trying to access it.
Variants of this message
  • has been destroyed but you are still trying to access it
  • MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it.
  • Your script should either check if it is null or you should not destroy the object.
  • MissingReferenceException: The object of type 'Animator' has been destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object.

What it means

You are holding a reference to a Unity object that has been destroyed. The C# reference still exists, but the native engine object behind it is gone, so any attempt to read or write its members fails. Unity distinguishes this from an ordinary null reference deliberately: the wording tells you the object was real at some point, which points at a lifetime problem rather than a missing assignment.

Causes and fixes

Ranked by how often they actually occur, most common first.

  1. A cached reference outlived the object it pointed at

    Storing a component or GameObject in a field and using it on later frames is normal and correct, right up until something destroys the target. The reference is not cleared for you. Enemy references held by a targeting system, and UI elements held by a controller, are the usual cases.

    The fix: Test the reference before use. Comparing a UnityEngine.Object to null returns true once it has been destroyed, so a plain null check is the correct guard here even though the C# reference is not really null.

    The null check works because Unity overloads == for UnityEngine.Object.

    using UnityEngine;
    
    public class Turret : MonoBehaviour
    {
        private Transform target;
    
        void Update()
        {
            // True once the target has been destroyed, even though the C#
            // reference is not literally null.
            if (target == null)
            {
                target = AcquireTarget();
                if (target == null) return;
            }
    
            transform.LookAt(target);
        }
    
        private Transform AcquireTarget() => null;
    }

    Source: Unity Discussions: MissingReferenceException on a destroyed Animator

  2. Work continued after Destroy was called

    Destroy does not remove the object immediately. Unity defers the actual destruction until after the current Update loop, so code running later in the same frame still sees the object, and code running on the next frame does not. A coroutine, an invoked method or a queued callback started before the destruction will therefore throw when it resumes.

    The fix: Return immediately after calling Destroy on the object you are working with. For deferred work, use DestroyImmediate only in Editor tooling, and otherwise cancel the pending work: stop the coroutine, unsubscribe the event, or check the reference again after any yield.

    A yield is a gap in which anything can be destroyed, including this component's own GameObject.

    using System.Collections;
    using UnityEngine;
    
    public class Pickup : MonoBehaviour
    {
        private IEnumerator CollectRoutine(Transform collector)
        {
            yield return new WaitForSeconds(0.5f);
    
            // The collector may have been destroyed during that half second.
            if (collector == null) yield break;
    
            collector.GetComponent<Inventory>()?.Add(this);
            Destroy(gameObject);
    
            // Nothing after a Destroy of this object should touch it.
        }
    }
    
    public class Inventory : MonoBehaviour
    {
        public void Add(Pickup pickup) { }
    }

    Source: Unity Scripting API: Object.Destroy, Unity Issue Tracker: MissingReferenceException when calling Destroy from a UI Button

  3. A scene load destroyed the object the reference pointed at

    Loading a scene in Single mode destroys everything in the previous scene. A manager marked DontDestroyOnLoad survives, but the references it holds to objects from the old scene do not. This is why the error so often appears the moment a game restarts or returns from a menu, on a script that worked perfectly until then.

    The fix: Re-acquire scene-owned references after each load rather than holding them across one. SceneManager.sceneLoaded is the hook for this, and clearing stale references at that point is more reliable than checking each one at the point of use.

    Persistent objects must treat every scene load as invalidating what they hold.

    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class GameManager : MonoBehaviour
    {
        private Transform player;
    
        void OnEnable() => SceneManager.sceneLoaded += HandleSceneLoaded;
        void OnDisable() => SceneManager.sceneLoaded -= HandleSceneLoaded;
    
        private void HandleSceneLoaded(Scene scene, LoadSceneMode mode)
        {
            // Anything from the previous scene is gone. Re-acquire rather than
            // trusting what was cached before the load.
            player = GameObject.FindWithTag("Player")?.transform;
        }
    }

    Source: Unity Discussions: MissingReferenceException after LoadScene on restart, Unity Scripting API: SceneManager.sceneLoaded

  4. An event or callback still holds the destroyed object

    Subscribing to a C# event or a UnityEvent creates a reference from the publisher to the subscriber. Destroying the subscriber does not remove that subscription, so the next invocation calls into a destroyed object. UI Button onClick listeners and static events are the usual sources, and static events are the worst because they persist across scene loads.

    The fix: Unsubscribe in OnDisable or OnDestroy, pairing every subscription with exactly one removal. Subscribing in OnEnable and removing in OnDisable keeps the two symmetrical and survives objects being disabled and re-enabled.

    Source: Unity Discussions: MissingReferenceException from a UI dropdown callback

How to prevent it

Treat every yield, await and callback boundary as a point where anything might have been destroyed. Re-check references after resuming rather than assuming the world is as you left it.

Pair subscriptions with removals in OnEnable and OnDisable. This single habit removes most dangling-reference errors, and it costs nothing.

Prefer object pooling to repeated Instantiate and Destroy for anything created frequently. A pooled object is deactivated rather than destroyed, so references to it stay valid and the error cannot arise.

Do not use DontDestroyOnLoad as a way to keep references alive across scenes. It keeps the manager alive, not the things it points at, and the resulting stale references are harder to trace than re-acquiring cleanly on load.

Unity version differences

The message and the underlying behaviour are unchanged across supported Unity versions. Unity has discussed deprecating the == null overload for UnityEngine.Object, which would change how destroyed objects compare, but as of Unity 6 the overload is still in place and a null check remains the correct guard.

Sources

Frequently asked questions

Why is it MissingReferenceException rather than NullReferenceException?
Unity distinguishes the two because they point at different mistakes. A UnityEngine.Object that has been destroyed keeps its C# wrapper, so the reference is not literally null; Unity detects that the native object behind it is gone and throws the more specific exception. The wording is a hint: the object existed, so the problem is lifetime rather than a missing assignment.
Does == null work on a destroyed object?
Yes. Unity overloads the equality operator for UnityEngine.Object so that a destroyed object compares equal to null, which is exactly what makes a plain null check the right guard. The catch is that this only applies to types deriving from UnityEngine.Object. A plain C# class holding a destroyed component will not behave this way, and the ?. operator bypasses the overload entirely.
Why can I not use the null-conditional operator?
Because ?. performs a genuine C# null check and does not go through Unity's overloaded operator. A destroyed object is not literally null, so ?. treats it as valid and calls the member anyway, throwing the exception you were trying to avoid. Use an explicit == null comparison for UnityEngine.Object types.
How do I find which object was destroyed?
The message names the type, and the stack trace names the script and line holding the reference. Where several references share a type, log the field names just before the failing line, or attach the object to the log call with Debug.Log(message, this) so clicking the console entry selects the right object in the Hierarchy.
Does Destroy remove the object immediately?
No. Unity defers destruction until after the current Update loop finishes, so the object remains usable for the rest of the frame. This is why code that destroys an object and then keeps working with it appears to succeed, and only fails on the following frame. DestroyImmediate does remove it at once, but it is intended for Editor tooling and is not safe during normal gameplay.

Last updated: