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
- Create a new expression instance for each target via `IBinding.CreateInstance(...)` (each IBinding represents a template that produces fresh instances).
- Do not cache or reuse binding expression instances across controls; create per-target.
- 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
- Call CreateInstance per target; never reuse an attached expression.
- Do not cache expression instances across controls.
- Treat IBinding as a factory, the expression as a single-use product.
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
- BindingExpression was already attached to a different proper
- BindingExpression has not been started.
- Cannot call AsObservable on a to binding expression which is
- Cannot subscribe to IndexerDescriptor.
- Cannot create ElementName binding when NameScope is null
AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13).
Data as JSON: /api/errors/8793ea0ecf56bc42.
Report an issue: GitHub.