Skip to content

UIDocument component causes infinite loop when serializing components #585

@MarkSpectarium

Description

@MarkSpectarium

Reading components from a GameObject with a UIDocument component causes an infinite loop that freezes the MCP client completely (cannot even cancel the operation).

Root Cause

The UIDocument.rootVisualElement property returns a VisualElement which has circular parent/child references (children[] → child elements →
parent → back to parent). The generic reflection-based serializer follows these references indefinitely.

How to Reproduce
Minimal Reproduction:

  1. Create a GameObject
  2. Add UIDocument component
  3. Assign a visualTreeAsset (.uxml file)
  4. Assign a panelSettings asset
  5. Call: mcpforunity://scene/gameobject/{instanceID}/components
  6. Result: MCP client freezes indefinitely

Note: The issue does NOT occur if either visualTreeAsset or panelSettings is unassigned.

Proposed Fix

Add special handling for UIDocument in GameObjectSerializer.GetComponentData() (similar to existing handling for Transform and Camera):

  // --- Special handling for UIDocument to avoid infinite loops from VisualElement hierarchy ---
  if (componentType.FullName == "UnityEngine.UIElements.UIDocument")
  {
      var uiDocProperties = new Dictionary<string, object>();

      try
      {
          // Get panelSettings reference safely
          var panelSettingsProp = componentType.GetProperty("panelSettings");
          if (panelSettingsProp != null)
          {
              var panelSettings = panelSettingsProp.GetValue(c) as UnityEngine.Object;
              if (panelSettings != null)
              {
                  var assetPath = AssetDatabase.GetAssetPath(panelSettings);
                  uiDocProperties["panelSettings"] = string.IsNullOrEmpty(assetPath)
                      ? panelSettings.name
                      : assetPath;
              }
          }

          // Get visualTreeAsset reference safely (the UXML file)
          var visualTreeAssetProp = componentType.GetProperty("visualTreeAsset");
          if (visualTreeAssetProp != null)
          {
              var visualTreeAsset = visualTreeAssetProp.GetValue(c) as UnityEngine.Object;
              if (visualTreeAsset != null)
              {
                  var assetPath = AssetDatabase.GetAssetPath(visualTreeAsset);
                  uiDocProperties["visualTreeAsset"] = string.IsNullOrEmpty(assetPath)
                      ? visualTreeAsset.name
                      : assetPath;
              }
          }

          // Get sortingOrder safely
          var sortingOrderProp = componentType.GetProperty("sortingOrder");
          if (sortingOrderProp != null)
          {
              uiDocProperties["sortingOrder"] = sortingOrderProp.GetValue(c);
          }

          // Get enabled state
          var enabledProp = componentType.GetProperty("enabled");
          if (enabledProp != null)
          {
              uiDocProperties["enabled"] = enabledProp.GetValue(c);
          }

          // NOTE: rootVisualElement is intentionally skipped - it contains circular
          // parent/child references that cause infinite serialization loops
          uiDocProperties["_note"] = "rootVisualElement skipped to prevent circular reference infinite loop";
      }
      catch (Exception e)
      {
          McpLog.Warn($"[GetComponentData] Error reading UIDocument properties: {e.Message}");
      }

      return new Dictionary<string, object>
      {
          { "typeName", componentType.FullName },
          { "instanceID", c.GetInstanceID() },
          { "properties", uiDocProperties },
          { "name", c.name },
          { "tag", c.tag },
          { "gameObjectInstanceID", c.gameObject?.GetInstanceID() ?? 0 }
      };
  }
  // --- End Special handling for UIDocument ---

File: Editor/Helpers/GameObjectSerializer.cs (insert after Camera special handling, around line 221)

I added that myself and it fixed that issue but might not be 100% fitting to your own code architecture.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions