Addressables.UpdateCatalogs returns Success even if UnityWebRequest fails

I am using Unity 2021.3.7f1 and Addressables 1.20.5.
I was using remote catalog, testing how Addressables handled network issues. I shut down the remote server, then called Addressables.UpdateCatalogs, the UnityWebRequest received 502 Bad Gateway internally, but the handle returned a Success and there was no error on the console. I found that there was a flag called “IgnoreFailures”, it was set to true by default when building remote catalog. But in my case, the remote catalog is necessary and cannot be ignored. We need remote catalog because we need to perform content update. Why is the error being ignored by default? And this flag is not even configurable, it was hard-coded in the build script. It’s in BuildScriptPackedMode.cs, line 530:

string[] dependencyHashes = null;
if (aaContext.Settings.BuildRemoteCatalog)
{
    dependencyHashes = CreateRemoteCatalog(jsonText, aaContext.runtimeData.CatalogLocations, aaContext.Settings, builderInput, new ProviderLoadRequestOptions() {IgnoreFailures = true});
}

Maybe it’s related to this issue: https://issuetracker.unity3d.com/issues/addressables-catalog-download-timeout-is-not-working

The reason why Catalog Download Timeout is not working is that Addressables dosen’t pass a ProviderLoadRequestOptions to TextDataProvider, so TextDataProvider uses a default setting:

// TextDataProvider.cs

// override input options with options from Location if included
if (m_PI.Location.Data is ProviderLoadRequestOptions providerData)
{
    m_IgnoreFailures = providerData.IgnoreFailures;
    m_Timeout = providerData.WebRequestTimeout;
}
else
{
    m_IgnoreFailures = rawProvider.IgnoreFailures;
    m_Timeout = 0;
}

As the code shows, when TextDataProvider uses default setting, the value of m_IgnoreFailures comes from rawProvider.IgnoreFailures, which is true.

I fixed this issue myself:

Index: ContentCatalogProvider.cs
===================================================================
--- ContentCatalogProvider.cs    (revision 75718)
+++ ContentCatalogProvider.cs    (revision 75719)
@@ -151,6 +151,16 @@
                     if (m_ProviderInterface.Location.Data is ProviderLoadRequestOptions providerData)
                         providerLoadRequestOptions = providerData.Copy();
+                    if (providerLoadRequestOptions == null)
+                    {
+                        if (ResourceManagerConfig.ShouldPathUseWebRequest(idToLoad) &&
+                            !(Application.platform == RuntimePlatform.Android && idToLoad.StartsWith("jar:")))
+                        {
+                            providerLoadRequestOptions = new ProviderLoadRequestOptions();
+                            providerLoadRequestOptions.WebRequestTimeout = Addressables.m_AddressablesInstance.CatalogRequestsTimeout;
+                        }
+                    }
+
                     if (loadCatalogFromLocalBundle)
                     {
                         int webRequestTimeout = providerLoadRequestOptions?.WebRequestTimeout ?? 0;

EDIT:
I created a project to test this issue, and found rawProvider.IgnoreFailures is false. So I think this issue is unrelated to my issue.

1 Like

In my case, it’s the providerData.IgnoreFailures was true. Because when building remote catalog, in BuildScriptPackedMode.cs, line 530, it assigned a ProviderLoadRequestOptions object to the Data field, the IgnoreFailures property was set to true. I was trying to write my own build script. But there were so many internal and private members, the only thing that I could do was just copying the entire Addressables’ source code to the Assets folder and modifying myself…

I created a project to test your issue, but can’t reproduce it. Can you confirm TextDataProvider uses a ProviderLoadRequestOptions to download catalog.json? I did this by adding some logs:

// TextDataProvider.cs
public void Start(ProvideHandle provideHandle, TextDataProvider rawProvider)
{
    m_PI = provideHandle;
    m_PI.SetWaitForCompletionCallback(WaitForCompletionHandler);
    provideHandle.SetProgressCallback(GetPercentComplete);
    m_Provider = rawProvider;

    ////----
    Debug.Log("TextDataProvider.InternalOp.Start");

    // override input options with options from Location if included
    if (m_PI.Location.Data is ProviderLoadRequestOptions providerData)
    {
        m_IgnoreFailures = providerData.IgnoreFailures;
        m_Timeout = providerData.WebRequestTimeout;

        ////----
        Debug.Log($"m_IgnoreFailures: {m_IgnoreFailures}, m_Timeout: {m_Timeout}");
    }
    else
    {
        m_IgnoreFailures = rawProvider.IgnoreFailures;
        m_Timeout = 0;

        ////----
        Debug.Log($"providerData is null, m_IgnoreFailures: {m_IgnoreFailures}, m_Timeout: {m_Timeout}");
    }
    var path = m_PI.ResourceManager.TransformInternalId(m_PI.Location);

    ////----
    Debug.Log(path);
   
    //...
}

For my project, the output is:

When calling Addressables.UpdateCatalogs, the first web request is to download the remote catalog.hash file. However, at this point, I have shut down the remote server, so it will fail and won’t download the catalog.json, but there will be no error, and Addressables will silently use the local catalog instead… I set the break point, I saw Addressables used ProviderLoadRequestOptions when downloading catalog.hash, and IgnoredFailures was true. I was referring to the catalog.hash, not catalog.json. Here is the screenshot:

8383014--1105527--Screen Shot 2022-08-23 at 4.33.36 PM.jpg

1 Like

I thought it’s about downloading catalog.json, not the .hash file. I was able to reproduce this issue.

I felt like I could write a whole essay about this case. I have tried numerous ways to try to deal with it, but bumped into issues after issues, come across several forum posts and issue trackers. I will summarize what I have found when I have spare time…

Finally, I have come up with a solution without modifying the source code. After calling Addressables.UpdateCatalogs, I call Addressables.CheckForCatalogUpdates again to check if there is any catalog update. If the update count is not zero, this implies that in the previous step, Addressables.UpdateCatalogs failed to update the catalog. The only problem of this solution is that we need to request the remote catalog.hash file twice, which is not a big deal, the file is only 32 bytes.
Before I came up with this solution, I have tried several ways to tackle this issue. However, each of them has its own problems.

  1. Inherit BuildScriptPackedMode and override the DoBuild method.
    Initially, I was trying to use the internal methods inside BuildScriptPackedMode, I had to copy many codes into Assets folder. But then I found that I only had to modify the “aaContext” thing, and change the IgnoreFailure flags to false, then write it again into the Library folder. I was able to set the IgnoreFailure to false and Addressables.UpdateCatalogs indeed printed out the network error. But the handle still returned a Success. What’s more, I also found that when there was a network error, the internal “catalog loading operation” was not released, and there was no way to release it. This was problematic because if I want to call Addressables.CheckForCatalogUpdates again, I have to make sure all handles related to loading catalog are all released. Otherwise, it will always return a list of string with zero elements. See:
    Unity Issue Tracker - UpdateCatalogs and CheckForCatalogUpdates don't register updates when addressables are built in another project

The internal “catalog loading operation” wasn’t released because I had set the IgnoreFailure flag to false. If I set it back to true, then the handle will be released though Addressables will omit the error. After that, I could use Addressables.CheckForCatalogUpdates without issue, it will return a list of string greater than zero if the Addressables.UpdateCatalogs failed.

Another thing also worth pointing out is that Addressables.CheckForCatalogUpdates is the only API that had been fixed, it will throw an exception if there is a network issue. So using this API is reliable.

  1. Writing my own AssetBundleProvider and getting the UnityWebRequest that is used to download the remote catalog.
    I have written my own Provider called “AssetBundleProviderAbortable”, but I found that when downloading the remote catalog.hash, Addressables still used the default “TextDataProvider”. And there was no option like the Addressables Group to which I could assign my own Provider. So this method doesn’t work.
1 Like

Hi all thanks for posting about this issue. I thought we fixed a similar issue in 1.20.5, but maybe we missed this use case. Is there a bug report created for this issue?

No, I haven’t filed a bug report regarding this issue. I saw the fix in 1.20.5 as well. But that wasn’t related to my issue. The fix in 1.20.5 was about fixing an issue where there was an exception occurred during the operation. However, mine is about the network error, which Addressables ignores it by default. So that was a separate issue.

Hi, I have filed a bug report now. The case number is IN-14770. Please take a look.

@better_walk_away Perfect thank you!

We’re using Unity 2021.3.24f1 with Addressables 1.19.19 and seem to have a very similar issue. I can’t find the ticket IN-14770 in the Issue Tracker (presumably it’s not public). Are you able to provide an update on the status of this bug @pillakirsten ?

@better_walk_away in the interim I’m looking into duplicating your workaround with the re-checking of catalog. When you say “… to check if there is any catalog update” are you referring to an update count internal to Addressables (e.g. via AsyncOperationHandle), or maintaining your own count via a field? Just trying to clarify how you’re keeping track of the number of updates requested; presumably if the AsyncOperationHandle.IsDone == true then you know the first download succeeded and no need to update again?

Thanks.