Search Unity

How to handle InvalidKeyException?

Discussion in 'Addressables' started by Philip_Zhang, Sep 3, 2019.

  1. Philip_Zhang

    Philip_Zhang

    Joined:
    Feb 15, 2017
    Posts:
    25
    I'm migrating from AssetBundle to Addressable.

    When loading an asset that is not in the bundle, in the AssetBundle workflow there will not be an exception and I can deal with the situation. But in the Addressable workflow, it will throw an InvalidKeyException and the game got stuck.

    I know I can first check if the key is valid by Addressables.LoadResourceLocationsAsync() first, but this will complicate the workflow which other programmers don't like.

    I digged in the source code and find that it is because of the Complete() method in AsyncOperationBase.cs, when AsyncOperationStatus.Failed, it will throw the exception. I commented this out and everything works fine. But I don't think this is how we're supposed to do :(

    I think it shouldn't throw an exception when the key is invalid in the first place.
     
  2. unity_bill

    unity_bill

    Joined:
    Apr 11, 2017
    Posts:
    1,053
    My memory is a little fuzzy, but I don't think we actually throw the exception. We log it, and then return it with the AsyncOperationHandle. Looking through the code quickly, this seems to be the case.

    So what exactly is breaking your game? Is it hanging?

    If you want to comment out that line, I have no problem with it. The reason we have an exception/log at all is so that you can know why the thing failed (as best as we can tell you). If you don't want to know, feel free to remove it.
     
    Last edited: Sep 5, 2019
  3. Philip_Zhang

    Philip_Zhang

    Joined:
    Feb 15, 2017
    Posts:
    25
    @unity_bill Thanks for the reply.

    The strange thing is I was using 1.1.9 and I was experiencing hanging so I posted this post. I upgraded to 1.1.10, the hanging disappeared but when I downgrade back to 1.1.9 the hanging doesn't exist anymore. I don't know what was happening lol.

    But anyway the problem disappeared now. It only logs an error and the game continues like you said, thanks.
     
  4. unity_bill

    unity_bill

    Joined:
    Apr 11, 2017
    Posts:
    1,053
    glad to hear it. If it does start happening again, a unity bug would be appreciated so we can try to track it down!
     
    TatRat likes this.
  5. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,188
    I believe logging an error is much less helpful than throwing an exception. The exception I could catch when I know that it might happen, but the error will forever spam the console, thereby making it impossible for me to use a failed attempt in my control flow. I don't see a reason why I would need to check the hard way if a resource exists when I can simply try to load and fail and deal with the result.
     
    neilsarkar and zkorosi like this.
  6. zkorosi

    zkorosi

    Joined:
    Jan 24, 2017
    Posts:
    4
    I agree, exceptions should be catchable. This isn't the only questionable decision in the addressables system tho'
     
  7. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    586
    Exceptions are catchable. If you don't want the extra internal error logs, you can turn that off in the addressable settings.
     
  8. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,188
    Not in the case of InvalidKeyException, I'm afraid. See this example:

    Code (CSharp):
    1. using System.Collections;
    2. using UnityEngine;
    3. using UnityEngine.AddressableAssets;
    4.  
    5. public class Test : MonoBehaviour
    6. {
    7.     IEnumerator Start()
    8.     {
    9.         var handle = Addressables.LoadAssetAsync<Sprite>("InvalidKey");
    10.         yield return handle;
    11.  
    12.         Debug.Log("Exception: " + handle.OperationException);
    13.     }
    14. }
    When calling LoadAssetAsync, no exception is thrown immediately. Instead, the call returns and continues until InvalidKeyException is thrown internally somewhere in the async loop within Addressables. This exception is then caught by Unity and forwarded as a Debug.LogError to the user. Now it may look like Addressables is smart enough to also store the exception in the AsyncOperationHandle.OperationException, but this object is unusable in practice. Inspecting the exception type reveals:

    Code (CSharp):
    1. System.Exception: ChainOperation of Type: UnityEngine.Sprite failed because dependent operation failed
    2. Exception of type 'UnityEngine.AddressableAssets.InvalidKeyException' was thrown., Key=MyKey, Type=UnityEngine.Sprite
    NotInvalidKeyException.png

    The exception object is actually a System.Exception which contains a string message that mentions the InvalidKeyException, but the original InvalidKeyException object is lost. This means, the best I can do is hope for Unity to never change the string message and search for a hopefully unambiguous match. Pretty hacky.
     
    Jaimi likes this.
  9. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    586
    Oh interesting, I thought they were setting it to the actual InvalidKeyException, or at least setting the InnerException to it. Then I agree with you, that's not very useful without the actual type.

    I have other gripes with the message for that, too. It will throw an exception if you try to load a <T> that does not exist but the key does exist in a different type, and the message doesn't tell you that. Also if you use LoadAssetsAsync, it will only print
    System.String[]
    for the key instead of including all the keys.
     
    Jaimi likes this.
  10. CameronND

    CameronND

    Joined:
    Oct 2, 2018
    Posts:
    88
    +1 to throwing exceptions. Let us developers handle it.
     
    Jaimi likes this.
  11. Noblauch

    Noblauch

    Joined:
    May 23, 2017
    Posts:
    275
    I have problems with the exceptions too. We are loading assets and if they don't exist we load a fallback asset that we know exist. I would like to check it myself, but the Addressables.LoadResourceLocationsAsync() didn't work out for me and I don't want to disable the errors in the settings, because I of course need to know if something unexpected failed.
    My own thread led me to this post.

    Do you have any advice?
    The exception messages itself are not useful as-well, I get something like:
    Exception encountered in operation UnityEngine.ResourceManagement.ResourceManager+CompletedOperation`1[System.Collections.Generic.IList`1[UnityEngine.Sprite]], result='', status='Failed': Exception of type 'UnityEngine.AddressableAssets.InvalidKeyException' was thrown., Key=System.Collections.Generic.List`1[System.Object], Type=UnityEngine.Sprite


    I would never find what caused it, because there is no address that wasn't found and no keys. So basically it's useless. We are still in the prototyping phase, but I already have massive problems finding the cause of the load errors.
     
    Jaimi likes this.
  12. ProtoTerminator

    ProtoTerminator

    Joined:
    Nov 19, 2013
    Posts:
    586
    That's why I manually log what I used to try to load the asset(s) if there was any error.
    Code (CSharp):
    1. Debug.LogWarning($"Error loading asset <{typeof(T)}> from address: {address}, label: {label}, error: {handle.OperationException}");
    So, even though I may not be able to handle the error in specific cases, I can at least see where it came from in the logs.
     
    Noblauch likes this.
  13. Midiphony-panda

    Midiphony-panda

    Joined:
    Feb 10, 2020
    Posts:
    243
    Sorry for the bump, but I agree with the other developers here.

    I don't understand why this is logged internally, as letting the user catch the InvalidKeyException would be super useful.

    Yes I can rely on the ResourceLocators to find whether a key is valid or not, but I feel like it defeats the user-friendliness of the LoadAssetAsync APIs.

    If a LoadAssetAsync can fail because of an InvalidKeyException, I think that should be in the hands of the developers to handle it.
     
  14. Midiphony-panda

    Midiphony-panda

    Joined:
    Feb 10, 2020
    Posts:
    243
    Eventually, for the users, Addressables API doesn't seem designed around "classic" C# try-catch paradigms. So, if you want to disable the logging of internal Addressables runtime exceptions and catch yourself the errors, you can do the following :

    1. Disable "Log Runtime Exceptions" in Addressable Asset Settings
    upload_2023-4-13_15-43-18.png

    2. Rely on the Status and OperationException of AsyncOperationHandles given by Addressables API, to handle errors your way.

    The potential drawback, of disabling Addressables runtime exception logs, is that you won't be able to see anymore every exceptions happening inside Addressables internals