AvaloniaUI/Avalonia · error · InvalidOperationException

{this} already has an owner.

Error message

{this} already has an owner.

What it means

ExpressionNode.SetOwner throws InvalidOperationException when Owner is already non-null. Each ExpressionNode belongs to exactly one BindingExpression for its lifetime; reusing a node in a second binding would corrupt both expressions' node chains. The guard prevents silent double-ownership.

Source

Thrown at src/Avalonia.Base/Data/Core/ExpressionNodes/ExpressionNode.cs:75

    public virtual void BuildString(StringBuilder builder, IReadOnlyList<ExpressionNode> nodes) 
    {
        if (Index > 0)
            nodes[Index - 1].BuildString(builder, nodes);
        BuildString(builder);
    }

    /// <summary>
    /// Sets the owner binding.
    /// </summary>
    /// <param name="owner">The owner binding.</param>
    /// <param name="index">The index of the node in the binding path.</param>
    /// <exception cref="InvalidOperationException">
    /// The node already has an owner.
    /// </exception>
    public void SetOwner(BindingExpression owner, int index)
    {
        if (Owner is not null)
            throw new InvalidOperationException($"{this} already has an owner.");
        Owner = owner;
        Index = index;
    }

    /// <summary>
    /// Sets the <see cref="Source"/> from which the node will read its value and updates
    /// the current <see cref="Value"/>, notifying the <see cref="Owner"/> if the value
    /// changes.
    /// </summary>
    /// <param name="source">
    /// The new source from which the node will read its value. May be 
    /// <see cref="AvaloniaProperty.UnsetValue"/> in which case the source will be considered
    /// to be null.
    /// </param>
    /// <param name="dataValidationError">
    /// Any data validation error reported by the previous expression node.
    /// </param>
    public void SetSource(object? source, Exception? dataValidationError)

View on GitHub (pinned to 11c5427268)

Solutions

  1. Create fresh ExpressionNode instances for each binding; never reuse a node across BindingExpressions.
  2. If pooling, ensure each node's Owner is cleared (or a new node created) before reuse.
  3. When copying a node chain, deep-clone the nodes rather than aliasing them.

Example fix

// before - reusing a node list across two expressions
var nodes = BuildNodes();
expr1.SetNodes(nodes);
expr2.SetNodes(nodes); // throws: already owned
// after
expr1.SetNodes(BuildNodes());
expr2.SetNodes(BuildNodes());
Defensive patterns

Strategy: validation

Validate before calling

// ExpressionNode instances must be uniquely owned. Never reuse across bindings.
if (node.Owner is not null)
    throw new InvalidOperationException("Node already owned; create a new instance.");

Type guard

static bool IsUnowned(ExpressionNode n) => n.Owner is null;

Prevention

When it happens

Trigger: Passing the same ExpressionNode instance into two different BindingExpression instances, or calling SetOwner twice on one node.

Common situations: Caching/sharing node instances to 'optimize' binding creation; a factory that returns pooled nodes without resetting ownership; a copy that aliases node references instead of cloning.

Related errors


AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13). Data as JSON: /api/errors/fdfafbc5c5f8acb2. Report an issue: GitHub.