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;
// RootView on GitHub (pinned to 225b0fbdb5)
Solutions
- Override BuildRoot and always return a non-null root TreeViewItem with depth == -1.
- When the data is empty, still return a valid root with an empty (not null) children list.
- 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
- Always return a non-null root from BuildRoot, even when data is empty.
- Set the root's depth to -1.
- Load async data before calling Reload.
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
- RefreshRows should set valid list of rows.
- root is null
- FindItem failed: root item has not been created yet
- TreeView: 'rootItem.children == null'. Did you forget to add
- Lightmapping.lightingSettings is null. Please assign it to a
AI-assisted analysis of Unity-Technologies/UnityCsReference@225b0fbdb5 (2026-08-13).
Data as JSON: /api/errors/b103f56ac7def358.
Report an issue: GitHub.