Search Unity

Serialization depth limit 7 exceeded...

Discussion in 'Scripting' started by LootlabGames, Mar 10, 2017.

  1. LootlabGames

    LootlabGames

    Joined:
    Nov 21, 2014
    Posts:
    343
    I am trying to see if anyone has an idea why I'm getting this error.
    I do not see any problems, maybe I am missing something.

    The object I am Serializing is CharacterData I only count a total of 3 levels.

    Error:
    Serialization depth limit 7 exceeded at 'MyGame::EquippedWeapons.rightHand'. There may be an object composition cycle in one or more of your serialized classes.

    Code (CSharp):
    1.     [Serializable]
    2.     public class CharacterData
    3.     {
    4.         [NonSerialized]
    5.         public Character owner;
    6.  
    7.         public string characterName;
    8.         public int characterLevel;
    9.         public Attributes attributes;
    10.  
    11.         public EquippedWeapons equippedWeapons = new EquippedWeapons();
    12.     }
    Code (CSharp):
    1.  
    2.     [Serializable]
    3.     public class EquippedWeapons
    4.     {
    5.         public EquippedWeaponInfo rightHand = new EquippedWeaponInfo();
    6.         public EquippedWeaponInfo leftHand = new EquippedWeaponInfo();
    7.     }
    Code (CSharp):
    1.  
    2.     [Serializable]
    3.     public class EquippedWeaponInfo
    4.     {
    5.         public float range = Constants.DEFAULT_MELEE_RANGE;
    6.         public float speed = 1;
    7.         public float minDamage = 1;
    8.         public float maxDamage = 1;
    9.         public float averageDamage = (1 + 1) / 2;
    10.         public WeaponAnimationType weaponAnimType = WeaponAnimationType.None;
    11.         public WeaponType weaponType = WeaponType.None;
    12.         public DamageType damageType = DamageType.Physical;
    13.         public List<WeaponAttackType> weaponAttackTypes = new List<WeaponAttackType>();
    14.         public int objectSoundsId = Constants.DEFAULT_WEAPON_OBJECT_SOUNDS_ID;
    15.      }
     
  2. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,698
    I count at least 7 so far:
    • [1] main object
      • [2] CharacterData
        • [3] EquippedWeapons
          • [4] EquippedWeaponInfo
            • [5] List<WeaponAttackType>
              • [6] WeaponAttackType
                • [7] fields in WeaponAttackType
    If WeaponAttackTypes contains anything other than primitives, you've exceeded Unity's frustratingly-small limit.
     
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    One of the most common culprits of this is that some type you serialize has a circular reference some where in its chain.

    A shallow circular reference is something like this:

    Code (csharp):
    1.  
    2. [Serializable]
    3. public class SomeType
    4. {
    5.     public SomeType Child;
    6. }
    7.  
    Because SomeType references a SomeType, it is circular.

    This could be even deeper... like:

    Code (csharp):
    1.  
    2. [Serializable]
    3. public class SomeType
    4. {
    5.     public SomeOtherType Child;
    6. }
    7.  
    8. [Serializable]
    9. public class SomeOtherType
    10. {
    11.     public SomeType Parent;
    12. }
    13.  
     
    Aleksandr_Novik and TonyLi like this.
  4. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,698
    ^ Related to that, this sneaky one got me long ago:
    Code (csharp):
    1. [Serializable]
    2. public class SomeType
    3. {
    4.     private SomeType child;
    5. }
    My thinking was that, since child is private, it shouldn't get serialized, right? Unfortunately it still raises the circular reference error because private and protected fields are still processed by the serialization pipeline, even if they're not included in the final serialized output.

    The fix is:
    Code (csharp):
    1. [Serializable]
    2. public class SomeType
    3. {
    4.     [NonSerialized] private SomeType child;
    5. }
     
  5. LootlabGames

    LootlabGames

    Joined:
    Nov 21, 2014
    Posts:
    343
    Thanks for the replies:

    It looks more like this
    • [1] CharacterData
      • [2] EquippedWeapons
        • [3] EquippedWeaponInfo
          • [4] List<WeaponAttackType>
            • [5] WeaponAttackType

    All the "Types" are just enums.
    I didn't know it was counting lists but I guess that makes sense.
    Still I don't see the 7...
     
  6. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,698
    As a test, you can trim it down until Unity stops complaining about the depth limit. Then, making sure your project's serialization is set to Force Text, use a text editor to open the .asset or .scene file that contains your CharacterData object. Count the number of indents. This might give you an insight into how many levels Unity's actually using.

    Could there be a circular reference (like lordofduct wrote about) in Attributes? You didn't post the code for that class.
     
    wpetillo and LootlabGames like this.
  7. LootlabGames

    LootlabGames

    Joined:
    Nov 21, 2014
    Posts:
    343
    I like the idea. But I'm unsure where I would locate the file you speak of.
    I get the warning before I even hit play.
     
    Last edited: Mar 10, 2017
  8. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,698
    First trim down your class definition until the warning goes away. You want the warning to go away so Unity can successfully serialize CharacterData and you can look at the result. Assuming the problem is that you're hitting the depth limit, your modified CharacterData should now serialize 7 levels deep. Save your scene.

    Then select Edit > Project Settings > Editor. Set Serialization to Force Text.

    Then open an OS file browser to the folder containing your .scene file. Open the .scene file in a text editor (e.g., Textpad on Mac, Notepad on Windows). You should see a text-based YAML representation of your scene. I'm assuming that CharacterData is a field inside a MonoBehaviour script that you've added to a GameObject in your scene.

    If it's a ScriptableObject asset, open the .asset file instead.
     
  9. LootlabGames

    LootlabGames

    Joined:
    Nov 21, 2014
    Posts:
    343
    So you helped me figure it out.
    I trimmed down the class so I could look at the scene file.
    After trimming it down and rebuilding the code I noticed it complaining about a different class now.
    That one was easy because it was a reference to an object I don't want to serialize.
    After fixing that I looked at the output and as expected it was 4 levels deep.
    So then I reverted to the original code and I'm not getting the warning anymore.

    I looks like Unity was unhappy about the other class that I fixed but was reporting this class as being the problem.
     
  10. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,698
    Glad you got it fixed! :)
     
  11. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    Holy cows. I can't believe this is still a thing! I have what would be a very simple and elegant script for copying the entire pose of a character as JSON... except that any reasonable character obviously has a deeper bone hierarchy than 7, and I hit this stupidly low limit.

    Sure, there should be some limit to avoid infinite recursion. But how about 1023? Or even 127? Why seven, fer cryin' out loud?

    Now my script is going to be neither simple nor elegant. Grr...
     
  12. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,911
    I can't for the life of me figure out why the serialization depth is not configurable.

    My thoughts on the matter are... pretend JsonUtility doesn't exist, and use Json.NET.
     
    lordofduct likes this.
  13. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,698
    It's not just JsonUtility. It's Unity's internal serialization.
     
    lordofduct likes this.
  14. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,911
    Oh crap - yikes!
     
    JoeStrout likes this.
  15. Flavelius

    Flavelius

    Joined:
    Jul 8, 2012
    Posts:
    945
    How to find out where this warning originates from? The warning is not really helpful

    Code (CSharp):
    1. Serialization depth limit 7 exceeded at 'UnityEngine.Events::ArgumentCache.m_ObjectArgument'. There may be an object composition cycle in one or more of your serialized classes.
    2.  
    3. Serialization hierarchy:
    4. 8: UnityEngine.Events::ArgumentCache.m_ObjectArgument
    5. 7: UnityEngine.Events::persistentCall.m_Arguments
    6. 6: UnityEngine.Events::persistentCallGroup.m_Calls
    7. 5: UnityEngine.Events::UnityEventBase.m_PersistentCalls
     
    Avalin and wpetillo like this.
  16. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,004
    Well, the limit of 7 is somewhat arbitrary, but the issue is that the amount of allocated objects does not scale linearly or quadratic but somewhat exponentially depending on the underlying structure. Have a look at this old blog post. Since custom serializable classes act like structs, with a depth limit of 1000+ you could end up with millions if not billions of objects which would probably just crash Unity. I'm pretty sure they ran a lot of tests in the early days what limit does still run somewhat stable. There are many workarounds to flatten the hierarchy of hierarchical data. One example is my ResourceDB class to store / serialize the path information of all files in the resources folder(s).

    The actual depth limit doesn't really matter since sooner or later someone will hit it. Such issues are hard to detect and when to decide to stop serializing. A good example is the billion laughs attack.


    Well, be careful with UnityEvents. They are built completely in C# and have already several levels of nesting internally. So you can not use UnityEvents in too deeply nested hierarchies.


    // UnityEvent (class)
    // \_ m_PersistantCalls (class)
    // \_ m_Calls (List)
    // \_ m_Arguments (class)
    // \_ data (native fields)


    So UnityEvents can only be used in shallow hierarchies.
     
    wpetillo likes this.
  17. Flavelius

    Flavelius

    Joined:
    Jul 8, 2012
    Posts:
    945
    Thx, that's what i suspected; it's not recursive, just too deeply nested which is unfortunate as it's probably due to using an animation controlling third party asset. 7 seems quite low as default in this case.
     
    JoeStrout likes this.
  18. TextusGames

    TextusGames

    Joined:
    Dec 8, 2016
    Posts:
    429
    upload_2020-12-23_21-40-0.png
    upload_2020-12-23_21-40-15.png

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. public class NewBehaviourScript : MonoBehaviour
    5. {
    6.     public C_0 c;
    7. }
    8.  
    9. [Serializable]
    10. public class C_0
    11. {
    12.     [SerializeField] private C_1 c_1;
    13. }
    14.  
    15. [Serializable]
    16. public class C_1
    17. {
    18.     [SerializeField] private C_2 c_2;
    19. }
    20.  
    21. [Serializable]
    22. public class C_2
    23. {
    24.     [SerializeField] private C_3 c_3;
    25. }
    26.  
    27. [Serializable]
    28. public class C_3
    29. {
    30.     [SerializeField] private C_4 c_4;
    31. }
    32.  
    33. [Serializable]
    34. public class C_4
    35. {
    36.     [SerializeField] private C_5 c_5;
    37. }
    38.  
    39. [Serializable]
    40. public class C_5
    41. {
    42.     [SerializeField] private C_6 c_6;
    43. }
    44.  
    45. [Serializable]
    46. public class C_6
    47. {
    48.     [SerializeField] private C_7 C_7;
    49. }
    50.  
    51. [Serializable]
    52. public class C_7
    53. {
    54.     [SerializeField] private C_8 C_8;
    55. }
    56.  
    57. [Serializable]
    58. public class C_8
    59. {
    60.     [SerializeField] private C_9 C_9;
    61. }
    62.  
    63. [Serializable]
    64. public class C_9
    65. {
    66.     [SerializeField] private C_10 C_10;
    67. }
    68.  
    69. [Serializable]
    70. public class C_10
    71. {
    72.     [SerializeField] private C_11 C_11;
    73. }
    74.  
    75. [Serializable]
    76. public class C_11
    77. {
    78.     [SerializeField] private C_12 C_12;
    79. }
    80.  
    81. [Serializable]
    82. public class C_12
    83. {
    84.     [SerializeField] private C_13 C_13;
    85. }
    86.  
    87. [Serializable]
    88. public class C_13
    89. {
    90.     [SerializeField] private int C_14;
    91. }
    92.  
    93. [Serializable]
    94. public class C_14
    95. {
    96.     [SerializeField] private int C_15;
    97. }
    98.  
    99. [Serializable]
    100. public class C_15
    101. {
    102.     [SerializeField] private int C_11;
    103. }
    104.  

    Limit is increases to 10 now.
     
    Bunny83 and losgatossuabes like this.
  19. szsahajsz

    szsahajsz

    Joined:
    Aug 17, 2018
    Posts:
    13
    not enof :)
     
    William_Aus likes this.
  20. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,930
    Not to continue posting in an old thread, but SerializeReference exists to solve this issue. You just have to code property drawers yourself.