Assertion failed on expression: 'IsInSyncWithParentSerializedObject()'

Hello,

I am trying to implement a property drawer that allows me to select an option between the different derived types of a base class. The problem I am facing is an assertion called IsInSyncWithParentSerializedObject that appears when I add a new array element in the editor as you can see in the pictures below:


I have just realized that the number of times this assertion is logged is proportional to the number of serialized properties of all the already existent array elements. In this example I initially had 2 array elements (4 properties), so when adding a new array element, the assertion is logged 4 times.

I leave below the current code I am using, just in case it helps where the root cause is:

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
using UnityEngine;
using Scripts;

namespace Editor
{
    [CustomPropertyDrawer(typeof(SubclassSelectorAttribute))]
    public class SubclassPropertyDrawer : PropertyDrawer
    {
        private Dictionary<string, Type> _typeNameToType;
        //private List<VisualElement> changeableElements;

        private List<string> _elements = new List<string>();

        public override VisualElement CreatePropertyGUI(SerializedProperty property)
        {
            _typeNameToType = new Dictionary<string, Type>();

            var baseType = fieldInfo.FieldType.IsArray ? fieldInfo.FieldType.GetElementType() : fieldInfo.FieldType;

            var derivedTypesNames = new List<string>();
            var derivedTypes = TypeCache.GetTypesDerivedFrom(baseType);
            foreach (var derivedType in derivedTypes)
            {
                derivedTypesNames.Add(derivedType.Name);
                _typeNameToType[derivedType.Name] = derivedType;
            }

            var rootElement = new VisualElement();
            var changeableElements = new List<VisualElement>();
            BuildUI(rootElement, property, derivedTypesNames, changeableElements);

            return rootElement;
        }

        private void BuildUI(VisualElement rootElement, SerializedProperty property, List<string> derivedTypes, List<VisualElement> changeableElements)
        {
            int dropDownInitValue = GetMananagedReferenceValueNameIdx(property, derivedTypes);

            var dropdown = new DropdownField("Derived Classes", derivedTypes, dropDownInitValue);
            dropdown.RegisterValueChangedCallback((evt) => OnDropDownValueChanges(rootElement, property, evt, changeableElements));
            rootElement.Add(dropdown);

            DisplayDerivedClassProperties(rootElement, property, derivedTypes[dropDownInitValue], changeableElements);
        }

        private int GetMananagedReferenceValueNameIdx(SerializedProperty property, List<string> derivedTypes)
        {
            if (!string.IsNullOrEmpty(property.managedReferenceFullTypename))
            {
                var name = property.managedReferenceFullTypename.Split('.').Last();
                return derivedTypes.IndexOf(name);
            }

            return 0;
        }

        private void DisplayDerivedClassProperties(VisualElement rootElement, SerializedProperty property, string value, List<VisualElement> changeableElements)
        {
            if (property.managedReferenceValue == null || property.managedReferenceValue.GetType() != _typeNameToType[value])
            {
                var derivedClassInstance = Activator.CreateInstance(_typeNameToType[value]);
                property.managedReferenceValue = derivedClassInstance;
                property.serializedObject.ApplyModifiedProperties();
            }

            var foldout = new Foldout();
            foldout.text = property.displayName;
            _elements.Add(property.displayName);

            if (property.hasChildren)
            {
                var enumerator = property.Copy().GetEnumerator();

                while (enumerator.MoveNext())
                {
                    var childProperty = enumerator.Current as SerializedProperty;
                    var propertyField = new PropertyField(childProperty, childProperty.displayName);
                    propertyField.BindProperty(childProperty);
                    foldout.Add(propertyField);
                }
            }

            rootElement.Add(foldout);
            changeableElements.Add(foldout);
        }

        private void OnDropDownValueChanges(VisualElement rootElement, SerializedProperty property, ChangeEvent<string> evt, List<VisualElement> changeableElements)
        {
            ResetDynamicVisualElements(rootElement, changeableElements);
            DisplayDerivedClassProperties(rootElement, property, evt.newValue, changeableElements);
            string text = string.Empty;
            foreach (var element in _elements)
            {
                text += $"{element}, ";
            }
            Debug.Log(text);
        }

        private void ResetDynamicVisualElements(VisualElement rootElement, List<VisualElement> changeableElements)
        {
            foreach (var element in changeableElements)
            {
                rootElement.Remove(element);
            }
            changeableElements.Clear();
        }
    }
}

Additionally, I have to more questions:

  1. I am commenting this line since all the array elements were sharing it:

    Why is this happening? Is this supposed to be the expected behavior? Due to this variable, I was facing problems in the ResetDynamicVisualElements method since it was trying to remove visual elements from a different array element property… finally I had to share this list across all the methods as an argument to avoid this problem.

  2. Modifying a string property and then adding an element to the array changes the array element label:


    Another mystery I cannot understand. I am binding the PropertyField to the Name SerializedProperty… how is this changing the element label?

I hope anyway can help me with this because I spent a lot of time into this and I cannot even realize where the root cause is.

Thank you very much in advance!

Anyone? :frowning:

I have just noticed that when assigning the method for displaying the fields to a button, the assertion does not always appear… could this be a Unity bug? I am starting to be out of ideas…

Even with this small code, the assertion is being raised.

        public override VisualElement CreatePropertyGUI(SerializedProperty property)
        {
            Debug.Log($"{property.displayName} managedReferenceValue is {property.managedReferenceValue}");

            var root = new VisualElement();

            if (property.managedReferenceValue == null)
            {
                property.managedReferenceValue = Activator.CreateInstance(typeof(DerivedClass1));
                property.serializedObject.ApplyModifiedProperties();
            }

            var foldout = new Foldout();
            foldout.text = property.displayName;
            var enumerator = property.Copy().GetEnumerator();
            enumerator.MoveNext();
            var childProperty = enumerator.Current as SerializedProperty;
            var propertyField = new PropertyField(childProperty, childProperty.displayName);
            foldout.Add(propertyField);
            root.Add(foldout);

            return root;
        }

Assertion failed on expression: ‘IsInSyncWithParentSerializedObject()’
UnityEditor.RetainedMode:UpdateSchedulers ()

Am I missing something?

This error was happening to me, in my case I had arrays exposed in the inspector that I was populating via a script,
I had to hide them in the inspector for the error to go away, this started to happen after I upgraded Unity
from 2022.3.16f1 to 2022.3.33f1, on the same project, not sure if it’s relevant but wasn’t happening before the upgrade,
I already had this script before upgrading.
Rope it helps
Regards

2 Likes

Same happening to me… Can anyone from Unity add anything about what may be going on here? Is it a Unity bug or is it something we are doing wrong?

Thank you! This worked for me in Unity 6.