Editing prefab causes references to it to become null until the object referencing it is reloaded

Hi there

I have been unable to reproduce this issue in a clean project with the ‘exact same’ setup.

In our project we have a monobehaviour, that monobehaviour references a scriptable object, and that scriptable object has a reference to a prefab.

On our monobehaviour, we have some editor functionality to instantiate that prefab at the position of the monobehaviour. This all works fine…

However, if I edit the prefab in any way (Turn Shadows off on the meshrenderer, turn on/off a component, anything), the reference to that prefab on the scriptable object becomes ‘null’ but ONLY as seen by my scripts. If I breakpoint my insantiation code, it will see that prefab reference as ‘null’. But if I view the scriptableobject in the inspector, it is NOT null. It has a valid reference to the prefab. Despite ‘viewing’ the scriptableobject in the inspector and confirming that the reference is not null, my code still thinks it is. Any attempts made by the Monobehaviour to access myScriptableObject.PrefabReference will return null.

I can fix this problem in one of a few ways…

  • I can clear the reference on the scriptableobject, and drag and drop the prefab back into the reference slot. This corrects the problem.
  • I can reload/recompile assemblies. This fixes the problem.

Once I ‘fix’ the problem using any of the 2 options, if I edit the prefab again, it’ll break again. It isn’t a permanent fix.

The follow doesn’t work:

  • Right clicking and reimporting the scriptable object. This doesn’t fix the problem.
  • Saving and reloading the scene. The monobehaviour in the scene still sees the prefab reference on the scriptable object as null. This doesn’t fix the problem.
  • Remove the scriptable object reference on the monobehaviour and drag and dropping the scriptable object back into field on the monobehaviour. This doesn’t fix the problem.
  • Our prefabs were upgraded from 2018.2… so to ensure the prefab itself wasn’t the problem I created a new prefab from scratch, and set that prefab as the referenced prefab in the scriptable asset. When I make changes to the prefab, I get the same issue. This doesn’t fix the problem.
  • Our scriptable objects were upgraded from 2018.2…to to ensure the scriptable object itself wasn’t the problem I created a new scriptable object from scratch and set that scriptableobject as the referenced scriptableobject on the monobehaviour. This doesn’t fix the problem.
  • Using both the scriptable object created from scratch, and making it reference the prefab that was also created from scratch doesn’t fix the problem either. So it doesn’t seem like there is an issue with the assets themselves.

When I breakpoint my code, and inspect the reference, it still shows me valid values. Note how the object is null in the second image, but we can still see my test variables ‘ShouldBe5’ and ‘ShouldSayTest’ reporting correct values. Also interesting to note is that the CachedPtr has become 0x0, though that doesn’t mean anything to me…

‘This’ is the scriptable object.
‘SpanPrefab’ is the reference to the prefab.

Before Editing ‘SpanPrefab’ Prefab:


After editing ‘SpanPrefab’ Prefab:

Thanks for bringing this to our attention. Any chance you could make the original project accessible to us and submit what you wrote here as a bug report?

Are your references by any chance in a List/Array/Collections?

No unfortunately. It’s just a direct reference to script on another prefab. As you can see in the image above, there is a script called ShapeFenceGenerator which has a reference to a FencePost prefab and a FenceSpan prefab. If I edit either of these, that prefab reference suddenly becomes null.

@LeonhardP I can give you access to our repository if you would like to pull it down and try for yourself. We do not want to publish our project via the bugtracker, since it is a bit of a black box, we don’t know who is going to have access and it’ll be up there in the bugtracker forever.

Could you please submit a bug report and reply with the issue ID? Then you can give the person handling your case access to your repository.

Has this issue been submitted? It’s super easy to reproduce.

Wondering this myself I came here because I have this same thing happening.

In my case I had a prefab on the scene that stored a list of other prefabs. When I edited the prefab from the project folder window, the prefab on the scene would get a null reference on the list and I’d have to slide the prefab back onto it.

This kept happening, until finally I “unpacked” the prefab that was holding the list script. Now this no longer is happening. I didn’t really need that to be a “prefab” anyways but it does seem like it would be easy to re-produce.

I should say though my project migrated over from previous version of Unity. Also while not intuitive I am able to select prefabs and tell them to all update/override at once, but they don’t all seem to update/override at the same time.

I kind of like the new system, but prefab references suddenly going null is a bit… scary=p

Yep we still have one stuburn prefab reference on one specific script that continues to go null. We just manually fix it whenever it happens. I can’t reproduce it and we do not want to upload and give an unknown Unity dev/qa person access to our entire game, so we just put up with it for now. I’ve tried exporting the specific scripts and the specific prefabs in question into another project to see if it happens there but no luck. Can’t reproduce.

We’re also getting this issue. This is really very fundemental, and can be reproduced with just two scripts.

One default Monobehaviour
One with a serialized field and a context menu to Debug.Log the field.

Add the 2nd script component to an object in the scene. Drag in a prefab reference to your serialized field (of type script 1). Log it - It won’t be null.

  1. Edit the prefab, and save it.

  2. Log it - Now it’s null.

We’ve submitted a detailed bug report including a repro project. We’ll need to delay releasing our next big asset until this is resolved :frowning:

2 Likes

Thanks you, appreciated!

If you post the case number here (when you have it) we can make sure to have a look soon next week.

1 Like

Sure thing (:

(Case 1116476) Prefab References are LOST when modifying prefab

I’ve also checked in the absolute latest official release and it’s still occurring. Cheers!

@DigitalSalmon

This is really weird. If you change the reference type in Spawner to GameObject or Transform it works fine. It is only when the type is a Monobehaviour the issue is reproducable.

I’ll have a look, but consider using GameObject as a work around for now

1 Like

I too have references in other scripts going missing when updating prefabs. Components randomly get new FileIDs for some reason according to my Git. This has happened since the beginning of new prefab workflows to the latest 2019 alpha13. Not sure if it’s due to a faulty upgrade process but this has been project breaking for months and needs attention.

3 Likes

If it helps - AssetDatabase.ForceReserializeAssets seems to help, as does marking things as dirty then saving the asset database. Useful in small cases, but not sustainable obviously!

Our project is a clean 2018.3 we’ve only been using for a few weeks so it’s not the upgrade process :slight_smile:

Thanks, this will help in the investigation of the issue.

Can you post your updated script here or PM to me?

Ahh afraid the use of those scripts was in a live project and has since been stripped out.

Broadly speaking, it looked like this

PrefabStage.prefabSaved += PrefabStage_PrefabSaved;

    public static void PrefabStage_PrefabSaved(GameObject gameObject) {
            string[] allAssetPaths = AssetDatabase.GetAllAssetPaths();
            IEnumerable<string> paths = allAssetPaths.Where(p => p.EndsWith(".prefab")).Where(p => AssetDatabase.LoadAssetAtPath<Module>(p) != null);
            paths = paths.Concat(allAssetPaths.Where(p => p.EndsWith(".prefab")).Where(p => AssetDatabase.LoadAssetAtPath<Blueprint.Blueprint>(p) != null));
            paths = paths.Concat(allAssetPaths.Where(p => p.EndsWith(".asset")).Where(p => AssetDatabase.GetMainAssetTypeAtPath(p) == typeof(Theme)));
           
            foreach (string path in paths) {
                Object loadedAsset = AssetDatabase.LoadMainAssetAtPath(path);
                EditorUtility.SetDirty(loadedAsset);
            }
           
            AssetDatabase.SaveAssets();
        }

Or instead of a dirty/save, we had previously had some success with ForceReserializeAssets, though I believe that was used in an AssetPostprocessor, or the OnWillSaveAssets in an AssetModificationProcessor.

Sorry I can’t be more specific - We tried many things, and hadn’t clocked what was actually happening until we’d gone through a few ‘fixes’.

Ok, I get it, after saving the prefab, you mark it dirty and save it again and you reference works correctly. Meaning I missed something in the prefab system to ensure MonoBehaviour references stays valid.

Thanks.

For posterity - Unity Issue Tracker - Prefab&#39;s references are lost when modifying prefab

Any updates on this? Pretty project breaking and has prevented us moving to the new prefab workflow / 18.3

1 Like

@DigitalSalmon and others.

It took at little while to figure out what is going but I figured it out.

This will be a little technical.

This is a side effect of prefabs becoming imported assets. Previously once a prefab was loaded it would never unloaded meaning references would stay just fine.

Now when a prefab is imported the current version in memory is unloaded, the asset is processed and a new version is loaded.

We make sure instanceid, guids and so an stay the same so any serialized references are not lost.

BUT! due to how Unity interacts with Mono you can have situations like this one. Where one MonoBehaviour is referencing another MonoBehaviour which basically is a simple pointer in Managed memory. Each MB have a c++ backend and it is this backend that is destroy an reallocated during import, which means it will be allocated at a new memory location. But the MonoBehaviour you already have in memory will still be pointing at the old memory location because it is using raw pointers and not the instance ids.

So in your case this now means you have reference to a managed MonoBehaviour without a native backend and thus it is null. (There was a blog post long ago about how we determine if a MonoBehaviour is null)

I will be working with our Scripting Team to find a solution to this problem.

For now the work around is to reference GameObjects instead, because they don’t have this problem. I know it sucks but until we have a solution for the underlaying problem this is all I can offer.

1 Like