Skip to content

Infinite hang when reloading a scene contains a manually spawned NetworkObject with a SpriteLibrary and a SpriteResolver #4095

Description

@acavailhez

Description

Infinite hang when reloading a scene contains a manually spawned NetworkObject with a SpriteLibrary and a SpriteResolver

Reproduce Steps

  1. Create an empty scene
  2. Add a NetworkManager, with UnityTransport
  3. Create a new prefab with NetworkObject, SpriteRenderer, Animator, SpriteLibrary, SpriteResolver (following basically this tutorial: https://www.youtube.com/watch?v=4xPHqEEa-V0)
  4. Create an empty object TestCrash with the following code, and link the prefab from (3) in the inspector
public class TestCrash : NetworkBehaviour
{
    [SerializeField] public GameObject prefab = null!;
    private bool _alreadyCalled;

    private void Start()
    {
        var networkManager = NetworkManager.Singleton;
        var unityTransport = networkManager.GetComponent<UnityTransport>();

        // Start local server
        unityTransport.UseWebSockets = false; // if applicable
        unityTransport.SetConnectionData("0.0.0.0", 7777);
        networkManager!.NetworkConfig.NetworkTransport = unityTransport;
        networkManager.StartHost();

        NetworkManager.Singleton.SpawnManager.InstantiateAndSpawn(
            prefab!.GetComponent<NetworkObject>(),
            OwnerClientId,
            destroyWithScene: true,
            position: Vector3.zero
        );
    }

    private void Update()
    {
        if (_alreadyCalled) return;
        if (Time.timeSinceLevelLoad < 3f) return;
        _alreadyCalled = true;
       NetworkManager.Singleton.SceneManager.LoadScene(SceneManager.GetActiveScene().name, LoadSceneMode.Single);
    }
}

TestCrash is some code that

  • starts a local server
  • spawns the prefab
  • waits 3 seconds
  • reloads the scene

Actual Outcome

After a few attempts, the scene never reloads and the Unity editor hangs forever (has to be force quitted)

Expected Outcome

The scene reloads every 3s

Environment

  • OS: Windows 11
  • Unity Version: 6000.4.7f1
  • Netcode Version: 2.12.0

Additional Context

  • This bug did not happen with 2.12.0 and Unity 6000.3.11f1 (started happening after upgrading to 6000.4.7f1)
  • The bug doesn't happen without destroyWithScene: true,
  • Removing the SpriteResolver/SpriteLibrary stops the bug from happening
  • The NetworkObject must be spawned with InstantiateAndSpawn, if the prefab is added to the scene, the bug doesn't happen

Here is a dump of the Main Thread stack when the editor is hanging. The CPU is at 0% at that point. Maybe a lock contention?

<unknown> 0x00007ff973d63cb4
<unknown> 0x00007ff973c30543
<unknown> 0x00007ff97107e558
UnityClassic::Baselib_SystemFutex_Wait(int * __ptr64,int,unsigned int) 0x00007ff8886af7f6
ProfilerMutexLock(Mutex & __ptr64,char const * __ptr64,profiling::Marker & __ptr64,char const * __ptr64,int) 0x00007ff88b1a4450
PersistentManager::Lock(PersistentManager::LockFlags,profiling::Marker * __ptr64) __ptr64 0x00007ff88b1a1b83
PersistentManager::GetPathName(EntityId) __ptr64 0x00007ff88b1968bb
GetGUIDFromEntityId(EntityId) 0x00007ff888e786fe
AssetChangedNotifier::UnsubscribeFromAll(EntityId) __ptr64 0x00007ff88a209aee
SpriteRenderer::MainThreadCleanup(void) __ptr64 0x00007ff88b631e42
delete_object_internal_step1(Object * __ptr64) 0x00007ff88b8cd380
CommitBatchDelete(BatchDelete & __ptr64) 0x00007ff88b5286d2
DestroyObjectHighLevel_Internal(Object * __ptr64,bool) 0x00007ff88b532e31
DestroyObjectHighLevel(Object * __ptr64,bool) 0x00007ff88b532ba8
DelayedDestroyCallback(Object * __ptr64,void * __ptr64) 0x00007ff88b7963cb
DelayedCallManager::Update(int) __ptr64 0x00007ff88b797ac8
<unknown> 0x00007ff88b4f9d6b
ExecutePlayerLoop(NativePlayerLoopSystem * __ptr64) 0x00007ff88b517f5b
ExecutePlayerLoop(NativePlayerLoopSystem * __ptr64) 0x00007ff88b51811a
PlayerLoop(void) 0x00007ff88b51d105
EditorPlayerLoop::Execute(void) __ptr64 0x00007ff889ab957a
PlayerLoopController::InternalUpdateScene(bool,bool) __ptr64 0x00007ff889ab2a9c
PlayerLoopController::UpdateSceneIfNeededFromMainLoop(void) __ptr64 0x00007ff889ab4dad
Application::TickTimer(void) __ptr64 0x00007ff889acc4c3
<unknown> 0x00007ff8894bfb2c
UnityMain 0x00007ff8894c8693
<unknown> 0x00007ff6ae972f2a
<unknown> 0x00007ff97360e957
<unknown> 0x00007ff973caad6c

Activity

  1. acavailhez commented on Jul 23, 2026

    @acavailhez
    Author

    Actually it seems like SpriteLibrary/SpriteResolver are not needed for the bug to happen, simply replacing the sprite triggers the bug:

    var go = NetworkManager.Singleton.SpawnManager.InstantiateAndSpawn(
            prefab!.GetComponent<NetworkObject>(),
            OwnerClientId,
            destroyWithScene: true,
            position: Vector3.zero
        );
    go.GetComponent<SpriteRenderer>.sprite = [other sprite]
    
  2. NoelStephensUnity commented on Jul 23, 2026

    @NoelStephensUnity
    Member

    Hi @acavailhez,

    Recent post above:

    Is this a typo?
    go.GetComponent<SpriteRenderer>.sprite = [other sprite]

    It should be this:
    go.GetComponent<SpriteRenderer>().sprite = [other sprite]

    Issue posted:

    Issue-1: Destroy with scene on a spawned instance just prior to performing a LoadSceneMode.Single scene loading event

    It looks like you are spawning a prefab instance in the currently active scene (where everything will be instantiated) and then immediately doing a scene loading event using LoadSceneMode.Single which will destroy anything in the currently active scene upon finishing the scene loading event (i.e. your spawned object is getting destroyed).

    Issue-2: For integrated scene management, use the NetworkSceneManager completed events or OnSceneEvent

    The supported way to know a netcode synchronized scene event is finished is to use the NetworkSceneManager events and not the UnityEngine.SceneManagement.SceneManager driven Time.timeSinceLevelLoad as that can lead to scenarios where UnityEngine.SceneManagement.SceneManager is finished but NetworkSceneManager is not.

    Issue-3: Using a NetworkBehaviour to start the NetworkManager

    We do not recommend using a NetworkBehaviour to start the NetworkManager and while it might work under certain conditions, it coudl also lead to unexpected results/issues. The recommend component to use for starting NetworkManager is to use a MonoBehaviour and I personally would recommend using the pattern of placing that on the same GameObject as the NetworkManager.

    Resolution fix

    Here is a quick modification of your script which should work.

    using Unity.Multiplayer.PlayMode;
    using Unity.Netcode;
    using Unity.Netcode.Transports.UTP;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    /// <summary>
    /// Add to your NetworkManager.
    /// </summary>
    /// <remarks>
    /// Don't use a NetworkBehaviour to start NetworkManager as NetworkBehaviours
    /// depend upon NetworkManager.
    /// </remarks>
    public class TestCrash : MonoBehaviour
    {
        public bool DestroyInstanceWithScene = false;
    
        [SerializeField] public GameObject PrefabToSpawn = null!;
        private NetworkManager m_NetworkManager;
    
        /// <summary>
        /// Requires: Multiplayer Playmode Tool
        /// Will start a host or client based on the editor window instance type.
        /// </summary>
        /// <returns>true or false as to whether the NetworkManager started.</returns>
        private bool QuickStart()
        {
            var unityTransport = m_NetworkManager.GetComponent<UnityTransport>();
    
            // This should be pre-configured but ok to do this as well.
            unityTransport.UseWebSockets = false; // if applicable
            unityTransport.SetConnectionData("0.0.0.0", 7777);
            m_NetworkManager.NetworkConfig.NetworkTransport = unityTransport;
    
            // Main editor view always starts as host
            if (CurrentPlayer.IsMainEditor)
            {
                return m_NetworkManager.StartHost();
    
            }
            else // MPPM virtual clients start as a client
            {
                return m_NetworkManager.StartClient();
            }
        }
    
        private void Start()
        {
            m_NetworkManager = GetComponent<NetworkManager>();
            if (m_NetworkManager == null)
            {
                Debug.LogError($"No {nameof(NetworkManager)} found! Add this component to the same {nameof(GameObject)} as your {nameof(NetworkManager)}!");
                return;
            }
            if (!QuickStart())
            {
                Debug.Log($"Failed to start {nameof(NetworkManager)} on {gameObject.name}!");
                return;
            }
    
            // If not destroying with the scene, then it is ok to spawn in the current scene prior to a scene loading
            // event of type  LoadSceneMode.Single (i.e. everything in the current active scene will be destroyed that
            // is marked to be destroyed).
            if (!DestroyInstanceWithScene)
            {
                // Ok to spawn an instance that doesn't destroy with a scene.
                SpawnPrefabInstance();
            }
    
            // Use the completed scene events to know everyone has finished loading and synchronizing the scene.
            m_NetworkManager.SceneManager.OnLoadEventCompleted += OnLoadEventCompleted;
            m_NetworkManager.SceneManager.LoadScene(SceneManager.GetActiveScene().name, LoadSceneMode.Single);
        }
    
        private void SpawnPrefabInstance()
        {
            if (PrefabToSpawn == null)
            {
                Debug.Log($"Will not spawn prefab instance as it is not set!");
                return;
            }
    
            NetworkObject.InstantiateAndSpawn(PrefabToSpawn, m_NetworkManager, destroyWithScene: DestroyInstanceWithScene, position: Vector3.zero);
    
        }
    
        private void OnLoadEventCompleted(string sceneName, LoadSceneMode loadSceneMode, System.Collections.Generic.List<ulong> clientsCompleted, System.Collections.Generic.List<ulong> clientsTimedOut)
        {
            Debug.Log($"[{sceneName}] Loaded via {loadSceneMode} scene mode.");
    
            // If you are destroying with the scene and immediately loading a scene using LoadSceneMode.Single,
            // then you need to spawn after the scene load to avoid having the newly spawned instance getting destroyed.
            if (DestroyInstanceWithScene)
            {
                SpawnPrefabInstance();
            }
        }
    }

    Let me know if this resolves your issues?

  3. added
    stat:awaiting-responseAwaiting response from author. This label should be added manually.
    and removed on Jul 23, 2026
  4. added
    type:supportQuestions or other support
    and removed
    stat:awaiting-triageStatus - Awaiting triage from the Netcode team.
    on Jul 23, 2026
  5. acavailhez commented on Jul 23, 2026

    @acavailhez
    Author

    Hello @NoelStephensUnity!

    About Issue 1

    TestCrash waits 3 seconds to reload the scene (it's not immediate)

    About Issue 2

    I don't think the issue is related to scene lifecycle, I changed TestCrash to wait 1 second before spawning the object and the bug still occurs

    About Issue 3

    When TestCrash is a MonoBehavior the issue still occurs

    Here's a simpler TestCrash to reproduce the issue:

    public class TestCrash : MonoBehaviour
    {
        [SerializeField] public GameObject prefab = null!;
        [SerializeField] public Sprite spriteToSwap = null!;
    
        private bool _alreadReloaded;
        private bool _alreadySpawned;
    
        private void Start()
        {
            StartHost();
        }
    
        private void Update()
        {
            SpawnInstanceAndSwapSpriteAfterOneSecond();
            ReloadSceneAfterThreeSeconds();
        }
    
        private void StartHost()
        {
            var networkManager = NetworkManager.Singleton;
            if (networkManager.IsListening) return;
    
            var unityTransport = networkManager.GetComponent<UnityTransport>();
            unityTransport.UseWebSockets = false;
            unityTransport.SetConnectionData("0.0.0.0", 7777);
            networkManager!.NetworkConfig.NetworkTransport = unityTransport;
            networkManager.StartHost();
            Debug.Log("Started host");
        }
    
        private void SpawnInstanceAndSwapSpriteAfterOneSecond()
        {
            // Wait 1 second and spawn
            if (Time.timeSinceLevelLoad < 1f) return;
            if (_alreadySpawned) return;
            _alreadySpawned = true;
            var spawned = NetworkManager.Singleton.SpawnManager.InstantiateAndSpawn(
                prefab!.GetComponent<NetworkObject>(),
                destroyWithScene: true
            );
            spawned.GetComponent<SpriteRenderer>().sprite = spriteToSwap;
            Debug.Log("Spawned object");
        }
    
        private void ReloadSceneAfterThreeSeconds()
        {
            // Wait 3 seconds then reload the current scene
            if (Time.timeSinceLevelLoad < 3f) return;
            if (_alreadReloaded) return;
            _alreadReloaded = true;
            Debug.Log("Reload scene");
            NetworkManager.Singleton.SceneManager.LoadScene(SceneManager.GetActiveScene().name, LoadSceneMode.Single);
        }
    }
    

    The first reload works, but the second reload hangs forever

  6. added and removed
    stat:awaiting-responseAwaiting response from author. This label should be added manually.
    on Jul 23, 2026
  7. NoelStephensUnity commented on Jul 23, 2026

    @NoelStephensUnity
    Member

    Here is an exported package that you can import into your project and it does exactly what you are trying to do but does not lock or crash the editor.
    Issue4095.zip
    Once imported, add the TestScene to your scenes in build list, open it, and enter play mode.
    The provided contents of the exported package continually reloads the scene every 3 second (what I had set as the default), changes the sprite on a newly spawned object, and will not cause any race conditions/crashes to occur.

    This line:
    NetworkManager.Singleton.SceneManager.LoadScene(SceneManager.GetActiveScene().name, LoadSceneMode.Single);

    Coupled with how you are checking if the scene is loaded is where you are running into issues.

    This code (based on my first response):
    if (Time.timeSinceLevelLoad < 1f) return;

    Is not a Netcode for GameObjects supported way to determine if a scene loading event has completed or not. This is actually a portion of the reason why you are having issues with your approach. I am just trying to point out that you are using a technique that we don't support so if you run into issues using this approach there is not much we can do to help you resolve that issue.

    The other issue is that you are reloading the same scene that has your NetworkManager within it...thus you are creating a new instance that is automatically migrated into the DDOL each time you reload the same scene.

    Here is a modified version of your project that has all sorts of catches in it where you could run into issues.
    The first issue it will run into is that you are creating more than one NetworkManager instance.
    It stops after the 1st scene load because it destroys the 2nd NetworkManager.
    If you were to adjust your code to assure that the 1st instance (pre-loaded scene) will create a Coroutine to reload the scene after (n) seconds (as is done in the script provided in the above exported package) then it will start working more as expected...but you still need to destroy that additional NetworkManager.

    using Unity.Netcode;
    using Unity.Netcode.Transports.UTP;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class TestCrashWithChecks : MonoBehaviour
    {
        private static bool AbortTest;
        [SerializeField] public GameObject prefab = null!;
        [SerializeField] public Sprite spriteToSwap = null!;
    
        private bool _alreadReloaded;
        private bool _alreadySpawned;
    
        private bool m_DoNotUpdate;
    
        private void Start()
        {
            m_LoadCounter++;
            if (AbortTest)
            {
                Debug.Log("Not starting host again as the test was aborted!");
            }
            var networkManager = GetComponent<NetworkManager>();
            if (NetworkManager.Singleton != null && NetworkManager.Singleton.IsConnectedClient && networkManager != NetworkManager.Singleton)
            {
                Debug.LogError("Multiple NetworkManagers detected! Destroying this new instance!");
                Destroy(gameObject);
                m_DoNotUpdate = true;
                return;
            }
            StartHost();
        }
    
        private void Update()
        {
            if (AbortTest || m_DoNotUpdate)
            {
                return;
            }
            SpawnInstanceAndSwapSpriteAfterOneSecond();
            ReloadSceneAfterThreeSeconds();
        }
    
        private void StartHost()
        {
            if (NetworkManager.Singleton != null &&  NetworkManager.Singleton.IsConnectedClient)
            {
                return;
            }
            var networkManager = NetworkManager.Singleton;
    
            var unityTransport = networkManager.GetComponent<UnityTransport>();
            unityTransport.UseWebSockets = false;
            unityTransport.SetConnectionData("0.0.0.0", 7777);
            networkManager!.NetworkConfig.NetworkTransport = unityTransport;
            if (networkManager.StartHost())
            {
                Debug.Log("Started host");
            }
            else
            {
                Debug.Log("Failed to start host");
                AbortTest = true;
            }
        }
    
        private void SpawnInstanceAndSwapSpriteAfterOneSecond()
        {
            // Wait 1 second and spawn
            if (Time.timeSinceLevelLoad < 1f) return;
            if (_alreadySpawned) return;
         
            try
            {
                _alreadySpawned = true;
                var spawned = NetworkManager.Singleton.SpawnManager.InstantiateAndSpawn(
                    prefab!.GetComponent<NetworkObject>(),
                    destroyWithScene: false
                );
                if (spawned != null)
                {
                    spawned.GetComponent<SpriteRenderer>().sprite = spriteToSwap;
                    Debug.Log("Spawned object");
                }
                else
                {
                    Debug.Log("Failed to spawn object!");
                    AbortTest = true;
                }
            }
            catch(System.Exception ex)
            {
                AbortTest = true;
                Debug.LogException(ex);
            }
        }
    
        private string m_SceneNameToLoad;
        private static int m_LoadCounter;
    
        private void ReloadSceneAfterThreeSeconds()
        {
            // Wait 3 seconds then reload the current scene
            if (Time.timeSinceLevelLoad < 3f) return;
            if (_alreadReloaded) return;
            
            if (m_LoadCounter > 1 && (!LastSceneLoadCompleted || m_DoNotUpdate) )
            {
                Debug.LogError("This would have tried to load a scene before it was finished!");
                return;
            }
            
            _alreadReloaded = true;
            LastSceneLoadCompleted = false;
            Debug.Log("Reload scene");
            NetworkManager.Singleton.SceneManager.OnLoadEventCompleted += SceneManager_OnLoadEventCompleted;
            m_SceneNameToLoad = SceneManager.GetActiveScene().name;
            var sceneLoadStatus = NetworkManager.Singleton.SceneManager.LoadScene(SceneManager.GetActiveScene().name, LoadSceneMode.Single);
            if (sceneLoadStatus != SceneEventProgressStatus.Started)
            {
                AbortTest = true;
                Debug.LogError($"Scene loading failed because: {sceneLoadStatus}");
            }
        }
    
        static bool LastSceneLoadCompleted;
    
        private void SceneManager_OnLoadEventCompleted(string sceneName, LoadSceneMode loadSceneMode, System.Collections.Generic.List<ulong> clientsCompleted, System.Collections.Generic.List<ulong> clientsTimedOut)
        {
            if (m_SceneNameToLoad == sceneName)
            {
                NetworkManager.Singleton.SceneManager.OnLoadEventCompleted -= SceneManager_OnLoadEventCompleted;
                LastSceneLoadCompleted = true;
            }
        }
    }

    You want to use m_NetworkManager.SceneManager.OnLoadEventCompleted to signal that everyone has loaded and processed everything within the newly loaded scene. We support that approach and if you run into issues using that approach we can help you resolve your issue or if it is a bug we can fix it.

    If you want to use this pattern (i.e. just reload the same currently loaded active scene over and over), then you would most likely want to use a "BootStrap" scene approach where: (I did not make these adjustments to your script)

    • The boot strap scene is your first scene loaded.
      • This contains your NetworkManager and other global/non-spawned managers attached to the same GameObject so they all end up in the DDOL and persist.
      • The TestCrash class would then use something like a Coroutine that reloads the same target scene after (n) seconds.
        • But not the boot strap scene...you would want a 2nd scene that you reload over and over.
    • The target scene to load should not contain NetworkManager or the thing that is continually reloading the same scene.

    (The example in the package I provided does not do this but does show that you can accomplish what you are trying to do if you use the supported way to handle detecting when a scene loading event has completed.)

    Here are some links to our documentation that you might find useful

    Don't use NetworkBehaviour to start NetworkManager

    Notifications for instantiating and destroying NetworkManager

    When is a scene truly loaded

    Using OnSceneEvent example

    Timing Considerations

    Let me know if this helps you resolve your issue?

  8. 8 remaining items

  9. added and removed
    stat:awaiting-responseAwaiting response from author. This label should be added manually.
    on Aug 11, 2026
  10. EmandM commented on Aug 11, 2026

    @EmandM
    Member

    Ah, I think I understand. Just to clarify, your core issue is that the Unity Editor is hanging, not that your Netcode logic isn't working? You're right that it's not expected that the Unity editor hangs forever in this case. We've not been able to reproduce the engine hang on our end.

    This github repo is for specifically Netcode for GameObjects issues. Seeing as this editor hang crosses a few product boundaries it's going to be easiest for us to address if you can report a bug in the editor. Feel free to link your case number here and we can follow up on your report to ensure it gets to the right place.

  11. added
    stat:awaiting-responseAwaiting response from author. This label should be added manually.
    and removed on Aug 11, 2026
  12. NoelStephensUnity commented on Aug 12, 2026

    @NoelStephensUnity
    Member

    @acavailhez
    I did replicate the issue and tried several combinations to resolve the issue. (Using 6000.4.0f1)
    It does appear (oddly enough) that only if you replace the sprite and mark the instance to not destroy with the scene load (single mode) then (on my side using Windows 11) it ends up freezing the editor (attaching with a debugger isn't very useful either under this scenario as well).

    However, if I comment out the code that swaps the sprite, then it runs perfectly fine. (🔥)

    As @EmandM mentioned, this does seem like an editor specific issue that you can file as an editor related bug report via the editor.

    However, we understand the pain associated with not being able to achieve something that does seem simple but ends up being an edge case issue. When using LoadSceneMode.Single, it places a lot of the control over to the SceneManager for things like when a scene is fully unloaded (and the assets within the scene) which could potentially expose a bug under very specific timing related circumstances.

    Typically, I recommend using an additive scene loading flow. This provides you with a bit more control over when you want to unload or load a scene and you can change the timing for both based on your project's needs.

    As such, here is an example project that accomplishes the same thing you were trying to do using LoadSeneMode.Single but it uses the additive scene loading pattern:

    GH-4095-Solution.zip

    Below is a video of the project running and shows you can switch the mode (destroy with scene or don't destroy with scene) on the fly in playmode and it continues to work whether destroying or not destroying when the scene it is currently within is unloaded (and it replaces the sprite on the newly instantiated and spawned instance):

    GH-4095-Solution.mov

    Let me know if this approach works for you (at least until the current issue when using SingleMode scene loading is resolved)?

  13. acavailhez commented on Aug 18, 2026

    @acavailhez
    Author

    Hi @NoelStephensUnity !

    Thanks a lot for your time and effort!

    I think additive scene loading is not going to work for what I'm doing, but I appreciate the idea.

    The lock contention only happens in the editor so I have a workaround for now. I'd to remove it one day!

  14. added and removed
    stat:awaiting-responseAwaiting response from author. This label should be added manually.
    on Aug 18, 2026
  15. NoelStephensUnity commented on Sep 25, 2026

    @NoelStephensUnity
    Member

    @acavailhez
    This has been identified as an engine issue and should be resolved (currently) in the following editor versions:
    6000.3.24f1, 6000.5.3f1 and 6000.6.

    No 6000.4 released build has this fix in it (you reported you were using 6000.4.7f1, which means the bug still exists on this version of Unity).

    My recommendation would be to update to 6000.5.3f1 or higher to resolve the underlying issue.

    Let me know if this (finally) resolves your issue?

  16. added
    stat:awaiting-responseAwaiting response from author. This label should be added manually.
    and removed on Sep 25, 2026
  17. michalChrobot commented on Sep 25, 2026

    @michalChrobot
    Member

    To add to that 6000.4 and 6000.5 are out of support already and won't receive further updates so preferably you would move to 6000.6 (although latest 6000.5 should also resolve your issue, just remember that the editor itself won't get further updates)

    With that I will close the issue but we can always reopen if this won't fix the issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    type:supportQuestions or other support

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions