GUID removal after deleting a file?

Hello,
I ran into a pretty weird behavior (from my point of view) when trying to check if an asset exists before creating one with the same name.

It works fine the first time the code is executed after opening Unity but if I delete manually the asset file in the Project window, Unity seems to keep the GUID in memory and tells me the asset still exists.


This is the code I made:
if (string.IsNullOrEmpty(AssetDatabase.AssetPathToGUID(_path)))
{
    AssetDatabase.CreateAsset(myObject, _path);
    EditorUtility.FocusProjectWindow();
    Selection.activeObject = myObject;
}
else
{
    Debug.LogError("There is already an asset with this name");
    Debug.Log(AssetDatabase.AssetPathToGUID(_path);
}

After first time I always have the error and an GUID in the console.


I tried `AssetDatabase.Refresh()` and nothing changed.

AssetDatabase.DeleteAsset(_path) returns false.

AssetDatabase.ImportAsset(_path)" gives me a warning in the console telling me the asset doesn’t exist.

But Debug.Log(AssetDatabase.GUIDToAssetPath(AssetDatabase.AssetPathToGUID(_path))) returns my “_path” value… (with the assetname.asset).

So I’m wandering:

  • Is there a way to clear/update the GUID of a project? Because it seems there is a hashtable or something somewhere that still stores the GUID and the path of a deleted .asset file.
  • Am I misunderstanding the use of assets, GUID and asset path?
  • Should I stop right here and use File.Exists of System.IO instead?

I sure wish I knew the answer to any of this.


One thing to note that AssetDatabase.DeleteAsset() returns false if the file wasn’t there to delete in the first place (… which can be pretty counter-intuitive, but there you go).


If you close Unity and restart it, it will forget about the guid-to-assetPath mapping.


My (sad) guess is that you understand things correctly, and there’s an oversight in the API because who would want to check if a thing exists? You can just know that it exists and then check the return value when you delete it. … I’d be happy to be proved wrong.


Here’s a cadre of other things I tried that didn’t seem to help (some of these are nonpublic methods you have to invoke via reflection; “results” only verified for 2018.4):

EditorUtility.UnloadUnusedAssetsImmediate(includeMonoReferenceAsRoots:false);
EditorUtility.UnloadUnusedAssetsImmediate(includeMonoReferenceAsRoots:true);
AssetDatabase.ReleaseCachedFileHandles();
AssetDatabase.SaveAssets();
AssetDatabase.CloseCachedFiles();
AssetDatabase.UnregisterAssetFolder();

And this is the Solution I ended up with. It works for deleted folders and a number of other common cases. I’m not 100% sure it works for everything, and I’m somewhat afraid there are going to be false positives… but it’s the best I’ve got (short of quitting and restarting Unity)

public static bool AssetActuallyExists(string assetPath)
{
    return !(AssetDatabase.AssetPathToGUID(assetPath) == string.Empty ||
        AssetDatabase.GetMainAssetTypeAtPath(assetPath) != null);
}

Test Code:

using NUnit.Framework;
using System;
using UnityEditor;
using UnityEngine;

namespace WhyWasThisSoDifficult
{
    [TestFixture]
    public class ExistenceTests
    {
        // Test Utility for creating, copying, and managing different Assets
        private class TestAsset
        {
            public string Path { get; private set; }
            public string DeletedPath
            {
                get
                {
                    var fileName = System.IO.Path.GetFileNameWithoutExtension(Path);
                    return Path.Replace(fileName, fileName + "_Deleted");
                }
            }

            public TestAsset(string path)
            {
                Path = System.IO.Path.Combine("Assets", path);
                AssetDatabase.CopyAsset(Path, DeletedPath);
                AssetDatabase.DeleteAsset(DeletedPath);
            }

            public TestAsset(string path, bool folder)
            {
                Path = path;

                AssetDatabase.CreateFolder("Assets", Path);
                AssetDatabase.CreateFolder("Assets", DeletedPath);

                Path = System.IO.Path.Combine("Assets", path);

                AssetDatabase.DeleteAsset(DeletedPath);
            }

            public TestAsset(UnityEngine.Object original, string path)
            {
                Action<UnityEngine.Object, string> CreateAsset =
                    (obj, p) => AssetDatabase.CreateAsset(obj, p);
                if(original.GetType() == typeof(GameObject))
                {
                    CreateAsset = (obj, p) => PrefabUtility.SaveAsPrefabAsset(obj as GameObject, p);
                }

                Path = System.IO.Path.Combine("Assets", path);
                CreateAsset(original, Path);

                UnityEngine.Object clone = UnityEngine.Object.Instantiate(original);
                CreateAsset(clone, DeletedPath);

                UnityEngine.Object.DestroyImmediate(clone, true);
                AssetDatabase.DeleteAsset(DeletedPath);
            }

            public bool Exists
            {
                get
                {
                    return !(AssetDatabase.AssetPathToGUID(Path) == null ||
                        AssetDatabase.GetMainAssetTypeAtPath(Path) == null);
                }
            }

            public bool DeletedExists
            {
                get
                {
                    return !(AssetDatabase.AssetPathToGUID(DeletedPath) == null ||
                        AssetDatabase.GetMainAssetTypeAtPath(DeletedPath) == null);
                }
            }
        }

        TestAsset prefab;
        TestAsset folder;
        TestAsset material;
        TestAsset text;
        TestAsset monoScript;
        TestAsset library;

        [OneTimeSetUp]
        public void SetupFixture()
        {
            // This can only really be done once, because AFAIK there's no way to get Unity back to a fresh state without restarting it.

            folder = new TestAsset("Folder", folder: true);
            prefab = new TestAsset(GameObject.CreatePrimitive(PrimitiveType.Cube), "Prefab.prefab");
            material = new TestAsset(new Material(Shader.Find("Standard")), "Material.mat");
            text = new TestAsset(new TextAsset("This is some text"), "Text.txt");
            monoScript = new TestAsset(new MonoScript() { }, "MonoScript.cs");
            library = new TestAsset(@"Packages/Runtime/OculusVR/Plugins/1.47.0/Win64/OVRPlugin.dll");
        }

        [OneTimeTearDown]
        public void TeardownFixture()
        {
            AssetDatabase.DeleteAsset(folder.Path);
            AssetDatabase.DeleteAsset(prefab.Path);
            AssetDatabase.DeleteAsset(material.Path);
            AssetDatabase.DeleteAsset(text.Path);
            AssetDatabase.DeleteAsset(monoScript.Path);
        }

        [Test]
        public void Created_Things_Exist()
        {
            Assert.IsTrue(prefab.Exists, "prefab didn't exist");
            Assert.IsTrue(folder.Exists, "folder didn't exist");
            Assert.IsTrue(material.Exists, "material didn't exist");
            Assert.IsTrue(text.Exists, "text didn't exist");
            Assert.IsTrue(monoScript.Exists, "monoScript didn't exist");
            Assert.IsTrue(library.Exists, "library didn't exist");
        }

        [Test]
        public void Deleted_Things_Dont_Exist()
        {
            Assert.IsFalse(prefab.DeletedExists, "deleted prefab _did_ exist!");
            Assert.IsFalse(folder.DeletedExists, "deleted folder _did_ exist!");
            Assert.IsFalse(material.DeletedExists, "deleted material _did_ exist!");
            Assert.IsFalse(text.DeletedExists, "deleted text _did_ exist!");
            Assert.IsFalse(monoScript.DeletedExists, "deleted monoScript _did_ exist!");
            Assert.IsFalse(library.DeletedExists, "deleted library _did_ exist!");
        }
    }
}

I’m probably way too late for this but for future people:
I got stuck on the same issue, for me it was related to addressables though and therefor I could not change the actual code that was calling the AssetPathToGUID code. In your case where u can however, u should be able to just specify OnlyExistingAssets option like so:

AssetDatabase.AssetPathToGUID("Assets/texture.jpg", AssetPathToGUIDOptions.OnlyExistingAssets);

For people having the same issue as I am having, restarting the unity editor doesn’t work (even though that is their solution on the unity page). I backed-up the folder that contained the asset that was giving the loading error, then deleted that folder in unity, restart unity, add the folder again, restart unity again (some restarts might not be necessary). This fixed the loading issue for me somehow. Note that this does create new GUIDs in some meta files but you can just revert these and it still works.