Addressables 1.5.0 "Invalid path in AssetBundleProvider" in iOS build, but not editor.

As the title suggests, I have addressable bundles that load fine in the editor, but when I build, the path provided by the AssetBundleProvider is incorrect. I’ve been beating my head against the wall with this one for a couple of weeks and I’m hoping someone can help. I’ve read similar threads on here, but their solutions were not mine. I’ll try to break this down as clearly and succinctly as possible.

I have two projects. Project A loads an addressable scene and its assets (multiple bundles) from Project B. Project A is a light, UI shell with scripts and Project B contains only the scene and its assets. There is a reason why I need to keep them separate, but the reason is irrelevant to this issue.

The addressable settings in Project B are as follows:

LocalBuildPath = [UnityEngine.Application.persistentDataPath]/addressables (I use the square brackets so the path is determined at build time.)

LocalLoadPath = {UnityEngine.Application.persistentDataPath}/addressables (I use the curly brackets so the path is determined at runtime.)

The group settings:


For “Asset Provider” and “AssetBundle Provider”, there are quite a few options, but not a whole lot of documentation on what each of them does. I’ve tried a number of different combinations.

To build, I go to:

Loading the addressable scene in Project A:

The bundles are downloaded from an S3 bucket to the persistent data path. Due to the nature of our project, I have chosen to download the bundles manually using AWS SDK for .NET instead of having addressables handle the download. When I manually place the files in the persistent data path for Project A and delete the original build files from Project B (so there is only one copy of each bundle on my machine), the addressables load fine in play mode. If I upload the bundles to S3 and delete all of them off my machine, the addressables download and load fine in play mode (so it’s not a download issue.) BUT, when I build the app for iOS, download the bundles and try to load the scene, I’m graced with this error:

Exception encountered in operation Resource(defaultlocalgroup_scenes_all_703a68b13fbcfdcb420946e7f32defc8.bundle): Invalid path in AssetBundleProvider: ‘[persistent data path to Project B]/addressables/defaultlocalgroup_scenes_all_703a68b13fbcfdcb420946e7f32defc8.bundle’.

So, the path it’s trying to load is the persistent data path of Project B instead of the persistent data path for the application which means that the path is hard-written somewhere and is not being determined at runtime. Yet, it loads the correct path in the Editor. Why?

If it’s helpful, here’s my code:

static List<string> loadedCatalogs = new List<string>();

    public static void LoadScene(string projectName, MonoBehaviour instance)
    {
        Addressables.InitializeAsync().Completed += (initOp) =>
        {
            if (!loadedCatalogs.Contains(projectName))
                instance.StartCoroutine(LoadCatalog(projectName, (catalog) =>
                {
                    loadedCatalogs.Add(projectName);
                    instance.StartCoroutine(LoadSceneAsync(projectName));
                }));
            else
                    instance.StartCoroutine(LoadSceneAsync(projectName));
        };
    }

public static IEnumerator LoadCatalog(string projectName, Action<IList<IResourceLocation>> callback)
    {
        Debug.Log($"Attempting to load catalog...");

        Debug.Log(GetCatalogPath(projectName));

        callback(Addressables.LoadResourceLocationsAsync(
            Addressables.LoadContentCatalogAsync(GetCatalogPath(projectName))).Result);
       
        yield return null;
    }

    public static IEnumerator LoadSceneAsync(string projectName)
    {
        Debug.Log($"Attempting to load scene...");

        var async = Addressables.LoadSceneAsync(projectName);

        while (!async.IsDone)
        {
            DialogueHandler.ShowLoadProgress(projectName, (long)(async.PercentComplete * 100));
            yield return null;
        }
    }

I didn’t post the code for GetCatalogPath(string), but it definitely returns the correct file path for the json catalog on all platforms. I’ve also tried this with and without Address.Initialize(). Not sure what that even does anymore as it works in the editor both ways.

I appreciate any and all help! Thanks in advance!

1 Like

Developing information: I just tried deleting Project B from my machine along with everything in its persistent data path and the addressables still load in Project A. This tells me that no files or settings from Project B are being referenced when loading the addressables in Project A. I also forgot to mention that the build target for the bundles is indeed set to iOS.

After weeks of frustration, it would figure that the day I finally decide to seek help from the forum is the day I figure out the problem on my own… at least mostly. I’ll post my findings below in case anyone else has the same problem.

In my project file for Project A → Library/com.unity.addressables/StreamingAssetsCopy/aa/iOS there was a file called catalog.json. This file was what was being loaded in the app that had the wrong file path. I’m not sure how it got there or why the information was incorrect, but Unity copies that file into Assets/StreamingAssets when building and then deletes it when the build completes. So it was included in the build and this is what my app was referencing when loading the addressables. I did a clean build in Project B and confirmed that the catalog.json file used {Application.persistentDataPath} instead of hard-referencing it’s own path and then copied that file to the same folder in Project A. That fixed the problem!

Ideally, though, my catalog would not be included in StreamingAssets when I build Project A. I tried removing it altogether, but that gave me different errors. If I run

Addressables.LoadContentCatalogAsync(filePath) to load the remote catalog from

filePaththen I want it to load that specific catalog. I don’t want it referring to a different catalog located in StreamingAssets. Maybe I’m just missing a fundamental principle of addressables, but that’s what would make sense to me. I feel like this approach to catalog loading will make it difficult when I’m trying to load addressables built from multiple different projects (which is where I’m heading) because the default name of the catalog is not unique (catalog.json). But that’s a different issue I’ll have to figure out.

Hope this helps someone else.

2 Likes

Because my specific use-case is not very well documented, yet, and I know I’m not the only one trying to figure this stuff out, I’m going to post some more of my findings that have given my success.

First, in my Project A project folder in Library/com.unity.addressables there was a folder called StreamingAssetsCopy. This is where the offending catalog.json was located. When building the application, it copied the contents into my StreamingAssets folder automatically. If I had been building addressables from Project A, this might have been necessary (not sure) but since I’m not, I deleted the folder entirely.

Secondly, I made some adjustments to my code…

Addressables.Initialize() isn’t necessary at least with version 1.5.0. At some point during development, they made Addressables.Initialize run automatically when you first make any Addressables call, so it runs when I load the catalog.

Also, Addressables.LoadResourceLocationsAsync() was giving me headaches every time I downloaded updated addressable bundles. I removed that line, as well.

My code currently looks like this:

static List<string> loadedCatalogs = new List<string>();

public static void LoadScene(string projectName, MonoBehaviour instance)
{
    Debug.Log($"void PortalAddressables.LoadScene({projectName}, {instance})");

    if (!loadedCatalogs.Contains(projectName))
        instance.StartCoroutine(LoadCatalog(projectName, (success) =>
        {
            loadedCatalogs.Add(projectName);
            instance.StartCoroutine(LoadSceneAsync(projectName));
        }));
    else
        instance.StartCoroutine(LoadSceneAsync(projectName));
}

public static IEnumerator LoadCatalog(string projectName, Action<bool> callback)
{
    Debug.Log($"Attempting to load catalog...");

    var async = Addressables.LoadContentCatalogAsync(GetCatalogPath(projectName));
    while (!async.IsDone) yield return null;

    Debug.Log("Catalog load complete...");

    callback(async.IsDone);
}

public static IEnumerator LoadSceneAsync(string projectName)
{
    Debug.Log($"Attempting to load scene...");

    var async = Addressables.LoadSceneAsync(projectName);

    while (!async.IsDone)
    {
        DialogueHandler.ShowLoadProgress(projectName, (long)(async.PercentComplete * 100));
        yield return null;
    }
}

Again, GetCatalogPath(projectName) returns the path to the catalog in my persistent data path. There may be a cleaner/ more efficient way to do all of this, but for now, it’s working beautifully. Hope this helps someone else.

2 Likes

Hey @JonathanBartel , apologies for no reply on our end for a while, this has been a great little post to read, glad it’s all working well for you!

I’ve sent this over to the team to see if there’s anything they want to add to this post!