dotnet/maui · error · InvalidOperationException

Cannot attach Effect to multiple sources

Error message

Cannot attach Effect to multiple sources

What it means

Thrown by Element.AttachEffect when the Effect being attached already has IsAttached == true. An Effect is a single-instance object tied to one element; once attached and its SendAttached called, attempting to attach it to a second element is illegal. This protects against sharing one Effect instance across multiple controls.

Source

Thrown at src/Controls/src/Core/Element/Element.cs:895

			{
				IReadOnlyList<Element> children = queue.Dequeue().LogicalChildrenInternal;
				for (var i = 0; i < children.Count; i++)
				{
					var child = children[i] as VisualElement;
					if (child == null || !child.IsVisible)
						continue;
					yield return child;
					queue.Enqueue(child);
				}
			}
		}

		void AttachEffect(Effect effect)
		{
			if (_effectControlProvider == null)
				return;
			if (effect.IsAttached)
				throw new InvalidOperationException("Cannot attach Effect to multiple sources");

			Effect effectToRegister = effect;
			if (effect is RoutingEffect re && re.Inner != null)
				effectToRegister = re.Inner;

			_effectControlProvider.RegisterEffect(effectToRegister);
			effectToRegister.Element = this;
			effect.SendAttached();
		}

		void EffectsOnClearing(object sender, EventArgs eventArgs)
		{
			foreach (Effect effect in _effects)
			{
				effect?.ClearEffect();
			}
		}

View on GitHub (pinned to f377ff1c5e)

Solutions

  1. Create a new Effect instance for each element — instantiate effects per-control rather than sharing.
  2. If sharing a RoutingEffect, verify re.Inner is null or also clone it.
  3. In a Replace notification, ensure the old effect is cleared (ClearEffect) before the new one is attached.

Example fix

// before
var effect = new MyRoutingEffect(); // shared
view1.Effects.Add(effect);
view2.Effects.Add(effect); // throws: already attached
// after
view1.Effects.Add(new MyRoutingEffect());
view2.Effects.Add(new MyRoutingEffect());
Defensive patterns

Strategy: validation

Validate before calling

foreach (var target in targets)
{
    if (effect.IsAttached)
        effect = CloneEffect(effect); // create a fresh instance
    target.Effects.Add(effect);
}

Type guard

static bool IsEffectFree(Effect e) => !e.IsAttached;

Try / catch

try { element.Effects.Add(effect); }
catch (InvalidOperationException ex) when (ex.Message == "Cannot attach Effect to multiple sources")
{
    element.Effects.Add(CloneEffect(effect)); // attach a fresh instance
}

Prevention

When it happens

Trigger: Adding the same Effect instance to more than one Element's Effects collection; reusing a single Effect object in multiple elements' Effects lists; adding an already-attached effect during a Replace collection-changed notification.

Common situations: Creating one Effect instance and adding it to multiple controls' Effects collections (a common mistake when factoring effects into shared fields); defining a RoutingEffect once and sharing it across a page's elements; effects loaded from a shared resource dictionary referenced by multiple views.

Related errors


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