m_Modification doesn't clear old references when the prefab is modified

I’ve got a bunch of prefabs with a script on them that used to have a field that’s been removed. Some of the prefab instances have modifications to that field. I thought that modifications to removed fields would automatically be removed when the prefab instance got reserialized due to something else changing, but it’s not happening.

The reason this is especially bad is that the field in question is a ScriptableObject, and that SO was stored as a part of the scene. Unity cleans out such SO’s when they’re not referenced anymore. SInce the modification to that field stays there, the SO won’t get cleared out.
In addition to all of this, we have deleted that SO’s type. This means that each of these SO’s are causing these warnings to appear in the console on startup:

The referenced script (Unknown) on this Behaviour is missing!
The referenced script on this Behaviour (Game Object '<null>') is missing!

In the scene file, this looks like this. I’ve got a prefab instance with this in it:

--- !u!1001 &1435652222
PrefabInstance:
  m_ObjectHideFlags: 0
  serializedVersion: 2
  m_Modification:
    ...
    - target: {fileID: 114238998614804306, guid: ff4165164015226499e68b7cb5fdc74e,
        type: 3}
      propertyPath: navLocation
      value:
      objectReference: {fileID: 218451484}

The “navLocation” field on the script has been deleted. This is one of several fields who’s overrides are not getting removed, but this is the one causing the most problems.

Further down the file is this line:

--- !u!114 &218451484
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 0}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: 35134fd8a34d47be92f33e58371449fc, type: 3}
  m_Name: Krathaven 9_Location
  m_EditorClassIdentifier:

35134fd8a34d47be92f33e58371449fc references a deleted script.

Things I have tried to clean this up:

  • Make changes to the prefab instance. In my experience, that causes the prefab instance to reserialize, Which usually fixes things like this, but no dice

  • Reserialize the entire scene asset, using AssetDatabase.ForceReserializeAssets. That doesn’t work either.

  • Delete the prefab instance, enter play mode, exit play mode, Undo.
    This half-solves the issue, as the SO is removed from the scene file. The prefab modification is still there, but now with objectReference: {fileID: 0}

Any advice? Is this a bug? What’s supposed to make prefab modifications that are not relevant anymore be cleared?

2 Likes

It’s not a bug, but we’re aware the design has some issues.

In general, Unity is very conservative with regards to deleting user data. For example, if you setup properties on a Material and later change the shader to a different one that doesn’t have those properties, we don’t delete the properties, and if you switch the shader back, your saved values are still there. This principle is used throughout lots of data in Unity.

For this Prefab case, say you remove that script property, but later revert that via version control or other means. Your old data is now back in the state you left it in. This is frequently convenient. The unused overrides data is still stripped from builds, since builds don’t has any concept of overrides at all, so it doesn’t cause overhead in that sense. But it does cause issues sometimes, and I think the fact that dependencies are still tracked for unused overrides is one such issue.

There are a few cases where we do remove unused overrides. For examples, if you have overrides on a component, but then remove that component (and applies it), then the overrides for that component are removed as well. But in more indirect cases, we don’t remove them.

Having tooling to allow cleaning up unused overrides is one of the things that are up for consideration for future improvements we’ll work on.

In our case, this is really bad, because the ScriptableObject type that the removed field is referencing has been removed from the project. Since Unity only removes ScriptableObjects that are a part of the scene when there’s no scripts removing them, those are permanently stuck there, and are puking warning messages into the console on startup.

The warning messages, together with the bloat of thousands of unused fields and ScriptableObjects cause our project size to bloat, and the warning messages also causes entering play mode to take a lot longer, since warning messages are slow. They also make it super hard to find real warning messages that we care about.

Unity also has a long-standing issue where you cannot delete nested assets who’s type is not known. If these were ScriptableObjects who’s type were known, I could probably fix this by doing LoadAllAssetsAtPath for the scene file, and deleting the instances I wanted gone. But in this instance that becomes impossible.

The warning message is also really, really hard to track down, as they look like this:

The referenced script (Unknown) on this Behaviour is missing!

and:

The referenced script on this Behaviour (Game Object '<null>') is missing!

There’s no context for any of these messages, and they’re wrong, as the referenced script is not a Behaviour, and not attached to any GameObject. I had to spend hours binary search deleting the objects in the scene in order to even understand what’s going on.

I think I can fix this, by creating a new, empty ScriptableObject, assigning it the GUID of the deleted ScriptableObject, and then search for instances of that to delete. That’s really, really hacky, and requires quite a lot of in-depth understanding of how Unity works - I think a lot of users would just be stuck with not being able to use warning messages for the rest of their project.

2 Likes

Yeah it sounds frustrating.

I forgot to be more clear that I meant that the fact that overrides are not removed is not a bug, but the fact that it causes referenced objects to be kept in the scene might still be. We’d appreciate a bug report on this.

It sounds like there’s also an unrelated issue with some of our warning messages that are not really related to Prefabs, but feel free to file bug reports about that too of course.

1 Like

I’ve reported both; 1191638 and 1191643.

2 Likes

Hi!

I eventually got both bugs back as “by design”, with mentions that a tool to clear the outdated references might be in the pipeline.

We just ran into this issue again in a different manner. We have some assets that used to be directly linked, but are now loaded from an asset bundle. So we went from:

public class Foo : MonoBehaviour {
    public Bar bar; // Bar is an asset file
}

To this:

public class Foo : MonoBehaviour {
    public string barID;
    private Bar bar;

    void Start() {
        var = GetFromAssetBundle(barID);
    }
}

The problem here is that Foo is on prefab instances, and prefab instances has old m_Modification’s that reference bar. This causes the Bar assets to be included both in the asset bundle, and in the game build itself, due to there being a reference from the scene the Foo instance is in, to the Bar asset.

That has to be an actual bug, right? We’re getting an asset included in a build, without having any editable references to that asset - all of our references are outdated m_Modification’s that we can’t really touch.

If I create a bug report showing this, any chance it’ll be handled? The m_Modification hanging around forever is sketchy, but fine. That that causes files that shouldn’t be included in builds to be included is pretty bad.

2 Likes

Hi,

I just wrote a long reply about how keeping property modification is by design and bla bla bla, but in the end you are right that should not cause assets to be included in the build. I’ll see if I can make a fix

2 Likes

Hey, thanks. If you want to, I can sit down and create a repro project for exactly that issue and create a bug report.

1 Like

I made a tool to cherry pick and clean up trash modifications.
Maybe this can help others too.
http://wiki.unity3d.com/index.php/PrefabDataCleaner

1 Like

Hey @steinbitglis_1
This looks great. We had - I believe - the same issue ( removing "dangling" dependencies - Unity Engine - Unity Discussions )

So I tried your tool :). Maybe I’m missing something, but it doesn not show me anything to clean up in my scene.

In the test project attached is a “test” script which exposed a
“PrefabDependency” field, which then got removed.
The assignment however persists on the Cylinder prefab (causing a “hidden dependency”):

PrefabDependency: {fileID: 611505733756841739, guid: 9d8d10dd541e6d94799fc2a7344510b4,
type: 3}

I was hoping this is something your tool would find and clean out, or am I mistaken?

Thanks in advance!

seb

5869477–624730–prefab_Depenencies.rar (80.7 KB)

In the project you’ve posted here, there is no prefab instance override data for the field that you have removed.
If you assign some data to the field (and save)., then remove it … then the window will pick it up and suggest removal.

1 Like

By the way, a similar issue exists for MonoScript default data,
http://wiki.unity3d.com/index.php/MonoScriptCleaner

1 Like

The cleaner script is awesome - but it would be good if it supported more than just prefabs in scenes!

But also… please fix this Unity!

Prefab instances in the project (nested prefab children) are also supported. Are there other things that need this?

@runevision What are the plans to fix this issue as I now have several prefab modifications in a scene that no longer need to be there? Is there an API call that can be made to remove currently invalid modifications?

1 Like

You would have to ask Steen who last commented on it here, since I’m not aware of the status:
https://discussions.unity.com/t/761895/7
He’s currently on vacation though.

In the mean time it looks like several people have found this workaround tool by steinbitglis useful:
https://discussions.unity.com/t/761895/9

Thanks for the reply @runevision . I took a look at the tool above, but it was easier to just manually edit the file and remove what was needed for now.

@SteenLund It would still be nice if there was a least an API call to clean a scene / prefab of invalid modifications. In our case we were left with a large orphaned array of assets still referenced from m_Modifications that I assume would still be loaded while in the editor.

Hi, I have spent some time on this now.

As previously mentioned it is by design that overrides are not removed from the scene files. We will be building some tooling to help you clean up overrides but for now we have other higher priority issues. As Rune mentioned above @steinbitglis_1 created a script that is very useful for this.

@Baste
We also discussed that these redundant overrides should not cause assets to be included in builds. I have tried to reproduce this in various setups and the only way I was able to reproduce this was when including scenes in asset bundles. In this case asset would be included due to the redundant overrides, but this is only a problem in the older asset bundle system. The dependencies are calculated correctly in the Scriptable Build Pipeline and redundant overrides are ignored.

Thanks!

I’ll look into if we’re using the old API or the new one - I think it’s a bit of both? We’re manually getting dependencies recursively in order to make some decisions, which is where I think this kicks in.

@Baste

If you are using AssetDatabase.GetDependencies(asset, false) to get direct dependencies then I just fixed a bug where prefabs would wrongly collect recursive dependencies. I will get this backported to 2020.2

1 Like