Unity-Technologies/UnityCsReference · critical · NullReferenceException

BuildRoot should set a valid root item.

Error message

BuildRoot should set a valid root item.

What it means

Thrown by ValidateRootItem (called after BuildRoot during FetchData) when the root item is still null. BuildRoot is a virtual method the subclass must override to construct and return the tree's root node; returning null (or not overriding it) leaves the tree empty and is a fatal data-source contract violation.

Source

Thrown at Editor/Mono/GUI/TreeView/TreeViewControl/TreeViewControlDataSource.cs:36

            {
                m_Owner = owner;

                // The user should just create the visible rows, we create the hidden root
                showRootItem = false;
            }

            public override void ReloadData()
            {
                // Clear root item to ensure client gets a call to BuildRoot every time Reload is called
                m_RootItem = null;
                base.ReloadData();
            }

            void ValidateRootItem()
            {
                if (m_RootItem == null)
                {
                    throw new NullReferenceException("BuildRoot should set a valid root item.");
                }
                if (m_RootItem.depth != -1)
                {
                    Debug.LogError("BuildRoot should ensure the root item has a depth == -1. The visible items start at depth == 0.");
                    m_RootItem.depth = -1;
                }
                if (m_RootItem.children == null && !m_Owner.m_OverriddenMethods.hasBuildRows)
                {
                    throw new InvalidOperationException("TreeView: 'rootItem.children == null'. Did you forget to add children? If you intend to only create the list of rows (not the full tree) then you need to override: BuildRows, GetAncestors and GetDescendantsThatHaveChildren.");
                }
            }

            public override void FetchData()
            {
                // Set before BuildRoot and BuildRows so we can call GetRows in them without recursion
                m_NeedRefreshRows = false;

                // Root

View on GitHub (pinned to 225b0fbdb5)

Solutions

  1. Override BuildRoot and always return a non-null root TreeViewItem with depth == -1.
  2. When the data is empty, still return a valid root with an empty (not null) children list.
  3. Ensure data loading completes before calling Reload.

Example fix

// before
protected override TreeViewItem<int> BuildRoot()
{
    if (myData == null) return null;
    // ...
}

// after
protected override TreeViewItem<int> BuildRoot()
{
    var root = new TreeViewItem<int>(0, -1, "root");
    if (myData != null)
    {
        foreach (var d in myData)
            root.AddChild(new TreeViewItem<int>(d.id, 0, d.name));
    }
    return root; // never null
}
Defensive patterns

Strategy: validation

Validate before calling

protected override TreeViewItem<int> BuildRoot()
{
    var root = new TreeViewItem<int>(0, -1, "root");
    // populate children...
    return root; // guaranteed non-null
}

Type guard

static bool IsValidRoot<T>(TreeViewItem<T> root) => root != null && root.depth == -1;

Prevention

When it happens

Trigger: Overriding BuildRoot but returning null; not overriding BuildRoot at all so the default returns null; BuildRoot returns null on a conditional path (e.g. when data is empty).

Common situations: Custom TreeView whose data source is empty/failed to load and BuildRoot returns null instead of an empty root; forgetting to implement BuildRoot in a new subclass; async data not yet loaded at Reload time.

Related errors


AI-assisted analysis of Unity-Technologies/UnityCsReference@225b0fbdb5 (2026-08-13). Data as JSON: /api/errors/b103f56ac7def358. Report an issue: GitHub.