dotnet/BenchmarkDotNet · error · InvalidOperationException
The current operation allowed for root nodes only, but the v
Error message
The current operation allowed for root nodes only, but the value {this} is attached to another node, {Owner}. What it means
Some CharacteristicObject operations are legal only on root nodes (objects with no Owner parent). AssertIsRoot enforces this by checking Owner == null. A non-root object is one that has been attached as a child of another CharacteristicObject and is therefore part of a frozen composite hierarchy.
Source
Thrown at src/BenchmarkDotNet/Characteristics/CharacteristicObject.cs:68
}
}
#endregion
#region Assertions
private void AssertNotFrozen()
{
if (Frozen)
{
throw new InvalidOperationException($"The current object {this} is frozen. Create a copy to modify.");
}
}
private void AssertIsRoot()
{
if (Owner != null)
{
throw new InvalidOperationException(
"The current operation allowed for root nodes only, " +
$"but the value {this} is attached to another node, {Owner}.");
}
}
private void AssertIsNonFrozenRoot()
{
AssertNotFrozen();
AssertIsRoot();
}
private static void AssertIsAssignable(Characteristic characteristic, object? value)
{
if (ReferenceEquals(value, Characteristic.EmptyValue) || ReferenceEquals(value, null))
{
if (characteristic.HasChildCharacteristics)
throw new ArgumentNullException(characteristic.Id);
View on GitHub (pinned to b515068b61)
Solutions
- Perform the operation on the root object (the Job/Config you own), not on a child/attached sub-object.
- Detach by creating a fresh root instance instead of reusing an attached one.
- Check obj.Owner == null before calling root-only APIs, or resolve the root via OwnerOrSelf.
- Build the object graph bottom-up and attach children last, so root operations happen before attachment.
Example fix
// before - operating on an attached child object child.SomeRootOnlyOperation(); // throws // after - operate on the root root.SomeRootOnlyOperation();
Defensive patterns
Strategy: validation
Validate before calling
if (characteristicObject.Owner != null)
throw new InvalidOperationException("Operation requires a root object; the given object is attached to a parent.");
// now safe to call root-only API Type guard
static bool IsRoot(CharacteristicObject o) => o.Owner == null;
Try / catch
try { obj.SomeRootOnlyOperation(); }
catch (InvalidOperationException ex) when (ex.Message.Contains("root nodes only"))
{
// resolve the root via OwnerOrSelf and operate there
} Prevention
- Operate only on the top-level Job/Config you own, never on attached child objects.
- Build the object graph bottom-up and attach children last.
- Check Owner == null before root-only operations.
When it happens
Trigger: Invoking a root-only operation (structural/identity mutations) on a CharacteristicObject that was previously attached as a child of another object, or operating on a sub-characteristic pulled out of a parent rather than on the top-level Job/config.
Common situations: Grabbing a nested characteristic object from a Job and trying to reconfigure it directly, or mixing detached and attached objects when building a config graph.
Related errors
- There is no default resolver for {characteristic.FullId}
- The current object {this} is frozen. Create a copy to modify
- There is no default resolver for {characteristic.FullId}
- The value {value} is not assignable to {characteristic} prop
- The argument must be an array
AI-assisted analysis of dotnet/BenchmarkDotNet@b515068b61 (2026-08-13).
Data as JSON: /api/errors/dc4cdfd74315c894.
Report an issue: GitHub.