Quick answer: The referenced asset is not included in the build — either the scene containing the prefab is missing from Build Settings > Scenes In Build, the asset lives in an Editor-only folder, or the reference is set at runtime via Awake() but another script reads it before Awake() completes. Ensure the scene is in the build, the asset is not Editor-only, and use OnEnable() ordering or ScriptableObject channels for cross-script data sharing.

Everything works in the Editor. You drag a reference into the Inspector, the field is populated, and the script runs without complaint. You make a build, run it, and immediately hit a NullReferenceException on a field you know you set. This particular failure mode catches almost every Unity developer at some point because the Editor hides a fundamental difference between “this is displayed in the Inspector” and “this will survive the build process.” Here is every reason it happens and exactly how to fix each one.

The Symptom

The stack trace in a build log (or a crash report from players) points to a line like myField.DoSomething() with a NullReferenceException. In the Editor, myField is clearly set in the Inspector. In the build, it is null. The field is decorated with [SerializeField] or is public, so serialization should handle it — and yet it does not.

A related symptom: the field is non-null in the first scene but null after a scene reload or after loading an additive scene. This points to a cross-scene reference issue rather than an asset inclusion issue, and the fix is different.

What Causes This

The asset is not included in the build. Unity’s build pipeline only bundles assets that are reachable from included scenes or placed in a Resources/ folder. If the object being referenced is in a scene that is not listed in Build Settings > Scenes In Build, or if it is an asset that only exists in the Editor project but is not actually referenced by anything in a built scene, Unity strips it. The Inspector will still show the reference in the Editor because the Editor has full access to the project’s Assets/ folder. The build does not.

The asset is Editor-only. Any asset inside a folder named Editor/ is excluded from builds by Unity’s compilation rules. If a [SerializeField] field references a ScriptableObject or prefab that lives under Assets/Editor/, it will be null in the build. Move the asset to a non-Editor folder.

Prefab variant with a missing override. Prefab variants can have field values that appear set in a nested prefab but are not overridden in the variant. When you open the scene in the Editor, Unity resolves the chain correctly. In certain build configurations, if the base prefab reference is broken, the variant inherits null. Always check the prefab variant’s Inspector with Override highlighting enabled to confirm the value is locally set.

Awake() race condition with another script. Unity does not guarantee execution order between Awake() calls on different objects unless you configure Script Execution Order in Project Settings. If Script A assigns a reference in its Awake() and Script B reads that reference in its own Awake(), Script B might run first:

// Script A — assigns the reference
public class ServiceLocator : MonoBehaviour
{
    public static ServiceLocator Instance;
    [SerializeField] private AudioManager audioManager;

    void Awake()
    {
        Instance = this;          // set too late if Script B runs first
    }
}

// Script B — reads the reference (may run before Script A's Awake)
public class PlayerController : MonoBehaviour
{
    void Awake()
    {
        // Null if ServiceLocator.Awake hasn't run yet
        ServiceLocator.Instance.DoSetup();
    }
}

The correct fix is to move the consuming call to Start() (which always runs after all Awake() calls) or to set Script A’s execution order earlier in Project Settings > Script Execution Order.

The Fix

Verify the scene is in Build Settings first — this catches the majority of cases. Open File > Build Settings, check the Scenes In Build list, and confirm that every scene whose prefabs or GameObjects you reference is included and enabled (checkbox ticked). If the scene is missing, drag it in from the Project window.

Next, check the asset path. If the referenced asset is under any folder named Editor, move it up one level. Then verify the prefab variant overrides as described above.

For the Awake race condition, the canonical Unity pattern is:

public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }

    [SerializeField] private InventorySystem inventorySystem;

    void Awake()
    {
        // Establish instance first, before anything can read it
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(gameObject);
    }
}

public class PlayerController : MonoBehaviour
{
    void Start()   // Start runs after all Awakes — safe to read Instance
    {
        GameManager.Instance.inventorySystem.Initialize();
    }
}

Addressables vs Resources vs Direct References

The three ways to load assets at runtime have different build implications:

“If you switch from direct references to Addressables mid-project, the old [SerializeField] references become dead weight — they still compile but the asset is no longer guaranteed to be loaded when your code runs. Audit every field that previously held a direct reference and replace it with an AssetReference plus an async load.”

Cross-Scene References

Unity’s serialization system cannot serialize a reference to a GameObject in a different scene. If you drag a scene object into an Inspector field and that object is in a different scene, Unity stores the reference for the current Editor session but loses it on save, reload, or build. At runtime the field will be null.

The correct patterns for sharing data across scenes are:

  1. ScriptableObject channels: Create a ScriptableObject that holds the data or raises events. Both scenes reference the same asset in their [SerializeField] fields. Assets survive scene loads.
  2. DontDestroyOnLoad singleton: A manager GameObject that persists across scene loads can hold references and be found via FindFirstObjectByType<T>() or a static Instance property.
  3. Static events: A static event Action<T> in a non-MonoBehaviour class decouples senders from receivers without requiring a scene reference.
// ScriptableObject event channel — works across scenes
[CreateAssetMenu(fileName = "PlayerDeathEvent", menuName = "Events/PlayerDeath")]
public class PlayerDeathEvent : ScriptableObject
{
    private System.Action onDeath;

    public void Subscribe(System.Action callback) => onDeath += callback;
    public void Unsubscribe(System.Action callback) => onDeath -= callback;
    public void Raise() => onDeath?.Invoke();
}

// Both scripts hold a [SerializeField] reference to the same .asset file
public class PlayerHealth : MonoBehaviour
{
    [SerializeField] private PlayerDeathEvent deathEvent;
    public void Die() => deathEvent.Raise();
}

Related Issues

If you confirm the field is correctly serialized and still see null, check whether Unity’s managed code stripping has removed the type. In Player Settings > Other Settings > Managed Stripping Level, try lowering from High to Minimal to rule out IL2CPP stripping the class. You can also add a link.xml file to preserve specific assemblies. A null [SerializeField] that appears after enabling IL2CPP is almost always a stripping issue rather than a reference issue.

Adding a null-guard assertion at the top of every method that touches a serialized reference will surface these bugs in your first QA run rather than in a player’s one-star review.