AvaloniaUI/Avalonia · error · InvalidOperationException

BindingExpression was already attached.

Error message

BindingExpression was already attached.

What it means

AttachCore wires a binding expression to exactly one sink/target. If `_sink` is already set, the expression is already attached and attaching again is an error, because a single expression instance cannot back two targets. This preserves one-to-one attachment semantics.

Source

Thrown at src/Avalonia.Base/Data/Core/UntypedBindingExpressionBase.cs:267

    /// <summary>
    /// When overridden in a derived class, writes the specified value to the binding source if
    /// possible.
    /// </summary>
    /// <param name="value">The value to write.</param>
    /// <returns>
    /// True if the value could be written to the binding source; otherwise false.
    /// </returns>
    internal virtual bool WriteValueToSource(object? value) => false;

    private void AttachCore(
        IBindingExpressionSink sink,
        ImmediateValueFrame? frame,
        AvaloniaObject target,
        AvaloniaProperty? targetProperty,
        BindingPriority priority)
    {
        if (_sink is not null)
            throw new InvalidOperationException("BindingExpression was already attached.");
        if (TargetProperty is not null && TargetProperty != targetProperty)
            throw new InvalidOperationException("BindingExpression was already attached to a different property.");

        _sink = sink;
        _frame = frame;
        _target = new(target);
        TargetProperty = targetProperty;
        TargetType = targetProperty?.PropertyType ?? typeof(object);
        Priority = priority;
    }


    /// <summary>
    /// Converts a value using a value converter, logging a warning if necessary.
    /// </summary>
    /// <param name="converter">The value converter.</param>
    /// <param name="converterCulture">The culture to use for the conversion.</param>
    /// <param name="converterParameter">The converter parameter.</param>

View on GitHub (pinned to 11c5427268)

Solutions

  1. Create a new expression instance for each target via `IBinding.CreateInstance(...)` (each IBinding represents a template that produces fresh instances).
  2. Do not cache or reuse binding expression instances across controls; create per-target.
  3. If you must bind many controls, loop and call CreateInstance for each.

Example fix

// before (reusing one expr):
var expr = myBinding.CreateInstance(a, prop, anchor);
expr.Start(...);
expr.Start(...); // throws on second attach

// after:
foreach (var c in controls)
    c.Bind(prop, myBinding); // CreateInstance per target
Defensive patterns

Strategy: validation

Validate before calling

// Each target gets its own expression instance.
var expr = binding.CreateInstance(target, prop, anchor); // attach once

Type guard

static bool IsUnattached(UntypedBindingExpressionBase e) => /* _sink */ false; // conceptual

Prevention

When it happens

Trigger: Calling Start/Attach twice on the same binding expression instance, or sharing one expression object across two AvaloniaObject targets.

Common situations: Reusing a single IBinding/Expression object to bind two different controls. Caching an expression instance and binding it again after it was already attached.

Related errors


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