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

  1. Perform the operation on the root object (the Job/Config you own), not on a child/attached sub-object.
  2. Detach by creating a fresh root instance instead of reusing an attached one.
  3. Check obj.Owner == null before calling root-only APIs, or resolve the root via OwnerOrSelf.
  4. 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

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


AI-assisted analysis of dotnet/BenchmarkDotNet@b515068b61 (2026-08-13). Data as JSON: /api/errors/dc4cdfd74315c894. Report an issue: GitHub.