(Case 1204074) BinaryFormatter is broken in mono backend (Unity 2019.2.9f1)

Got an error caused by the BinaryFormatter upon deserialization of a large file.

This is the exception (which actually is missing a proper output, due to the bug in mono):

Parameter name: field Stack:   at (wrapper managed-to-native) System.TypedReference.MakeTypedReferenceInternal(object,System.Reflection.FieldInfo[])
  at System.TypedReference.MakeTypedReference (System.Object target, System.Reflection.FieldInfo[] flds) [0x00123] in <599589bf4ce248909b8a14cbe4a2034e>:0
  at System.Runtime.Serialization.ObjectManager.DoValueTypeFixup (System.Reflection.FieldInfo memberToFix, System.Runtime.Serialization.ObjectHolder holder, System.Object value) [0x0014d] in <599589bf4ce248909b8a14cbe4a2034e>:0
  at System.Runtime.Serialization.ObjectManager.CompleteObject (System.Runtime.Serialization.ObjectHolder holder, System.Boolean bObjectFullyComplete) [0x0021a] in <599589bf4ce248909b8a14cbe4a2034e>:0
  at System.Runtime.Serialization.ObjectManager.DoNewlyRegisteredObjectFixups (System.Runtime.Serialization.ObjectHolder holder) [0x00040] in <599589bf4ce248909b8a14cbe4a2034e>:0
  at System.Runtime.Serialization.ObjectManager.FixupSpecialObject (System.Runtime.Serialization.ObjectHolder holder) [0x000c0] in <599589bf4ce248909b8a14cbe4a2034e>:0
  at System.Runtime.Serialization.ObjectManager.DoFixups () [0x0005a] in <599589bf4ce248909b8a14cbe4a2034e>:0
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize (System.Runtime.Remoting.Messaging.HeaderHandler handler, System.Runtime.Serialization.Formatters.Binary.__BinaryParser serParser, System.Boolean fCheck, System.Boolean isCrossAppDomain, System.Runtime.Remoting.Messaging.IMethodCallMessage methodCallMessage) [0x00077] in <599589bf4ce248909b8a14cbe4a2034e>:0
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler, System.Boolean fCheck, System.Boolean isCrossAppDomain, System.Runtime.Remoting.Messaging.IMethodCallMessage methodCallMessage) [0x000a2] in <599589bf4ce248909b8a14cbe4a2034e>:0
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler, System.Boolean fCheck, System.Runtime.Remoting.Messaging.IMethodCallMessage methodCallMessage) [0x00000] in <599589bf4ce248909b8a14cbe4a2034e>:0
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler, System.Boolean fCheck) [0x00000] in <599589bf4ce248909b8a14cbe4a2034e>:0
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler) [0x00000] in <599589bf4ce248909b8a14cbe4a2034e>:0
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream) [0x00000] in <599589bf4ce248909b8a14cbe4a2034e>:0

This is an issue that was fixed in newer mono versions (BinaryFormatter deserialize fails with System.ArgumentException · Issue #13162 · mono/mono · GitHub | Using BinaryFormatter to deserialize an array in a struct in a parent class fails with unfriendly exception [reproducible test case i promise] · Issue #8915 · mono/mono · GitHub), but since Unity uses older fork, it never got fixed.

Please address this issue, and update Unity’s mono backend, or include the fix.

1 Like

You should probably send a bug report.

2 Likes

Unfortunately I cannot pin down whats actually causing this to make a minimal repro.
It is just another class that inherits from class → abstract class hierarchy.
One of the like 38 of them and this one just breaks deserializer for no apperent reason.

Top class (breaks deserializer):

   [System.Serializable]
   public class EnvSpawnerPropPackage : EnvironmentPropPackage {
      public List<string> SpawnTableIds = new List<string>();
      public int SpawnTier;
   }

Middle one, works just fine:

   [System.Serializable]
   public class EnvironmentPropPackage : SaveDataPackage {
      public float MaxHealth;
      public float CurrentHealth;

      public bool CanTakeDamage;
      public bool IsDestroyedBefore;
      public bool IsActive;

      public RigidbodyPackage RigidbodyPackage;
     
      /*
      [System.Serializable]
       public struct RigidbodyPackage {
          public Vector3 Position;
          public Quaternion Rotation;

          public Vector3 Velocity;

          public float AngularDrag;
          public Vector3 AngularVelocity;
          public float Drag;
          public bool FreezeRotation;
          public bool IsKinematic;
          public float Mass;
      } */
   }

Base class:

   [Serializable]
   public abstract class SaveDataPackage {
      public string SceneId;
      public string PrefabId;

      public bool IsSeparateObject;
   }

Added a lot of checks to ensure that list is not empty or null, and its not.
Added checks for the ids to be not null or empty.
I’ve tried removing fields from the child class, and it still breaks.

Removing that class from serialization makes deserialization work again.

I’ve got no clue. I’ll try out making same layout in an empty project tomorrow to see if it acts the same.
Maybe deserializer will act the same there. Then I’ll submit it.

Okay, yes it is reproducable as minimal repro in a separate project. Submitted, case 1204074.

1 Like

If anyone hits this, as a temporary solution I suggest looking into (Newtonsoft).Bson.
Requires no changes to the data code base and it supports abstract classes without any workarounds.

Also quite easy to replace binary serializer with it.

(Well, actually any serializer will probably work out nicely, I just didn’t found one anything better for my code base)