stride3d/stride · error · InvalidOperationException

A dependency object is already attached to this instance of…

Error message

A dependency object is already attached to this instance of DependencyPropertyWatcher.

What it means

DependencyPropertyWatcher.Attach can hold exactly one dependency object at a time. Calling Attach on a watcher that already has a frameworkElement attached throws InvalidOperationException, since attaching another object would lose the Loaded/Unloaded subscriptions and change handlers of the previous target.

Solutions

  1. Create a new DependencyPropertyWatcher instance for each dependency object.
  2. Detach or dispose the existing watcher (remove handlers / null out frameworkElement) before attaching a new object.
  3. Keep one watcher per view instead of storing watchers in shared/static state.

Example fix

// before
watcher.Attach(oldElement);
watcher.Attach(newElement); // throws
// after
watcher = new DependencyPropertyWatcher(path, onChanged);
watcher.Attach(newElement);
Defensive patterns

Strategy: validation

Validate before calling

if (watcher != null && watcher.IsAttached)
    watcher = new DependencyPropertyWatcher(path, onChanged);
watcher.Attach(dependencyObject);

Type guard

bool CanAttach(DependencyPropertyWatcher w) => w == null || !w.IsAttached;

Try / catch

try { watcher.Attach(dependencyObject); }
catch (InvalidOperationException ex) when (ex.Message.Contains("already attached")) { watcher = new DependencyPropertyWatcher(path, onChanged); watcher.Attach(dependencyObject); }

Prevention

When it happens

Trigger: Calling watcher.Attach(dependencyObject) twice on the same watcher instance with different (or any) dependency objects while frameworkElement is already non-null and the new object is not the same reference.

Common situations: Reusing a single watcher across pages/controls without detaching; subscribing the watcher in a constructor that runs again after re-templating; holding watchers in static fields shared by multiple views.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/8090c5f4c8056b70. Report an issue: GitHub.

Appendix: source

Thrown at sources/presentation/Stride.Core.Presentation.Wpf/Core/DependencyPropertyWatcher.cs:39

        public DependencyPropertyWatcher()
        {
        }

        public DependencyPropertyWatcher([NotNull] FrameworkElement attachTo)
        {
            Attach(attachTo);
        }

        public DependencyObject AssociatedObject => frameworkElement;

        public void Attach([NotNull] DependencyObject dependencyObject)
        {
            if (dependencyObject == null) throw new ArgumentNullException(nameof(dependencyObject));
            if (ReferenceEquals(dependencyObject, frameworkElement))
                return;

            if (frameworkElement != null)
                throw new InvalidOperationException("A dependency object is already attached to this instance of DependencyPropertyWatcher.");
            frameworkElement = dependencyObject as FrameworkElement;

            if (frameworkElement == null)
                throw new ArgumentException("The dependency object to attach to the DependencyPropertyWatcher must be a FrameworkElement.");

            frameworkElement.Loaded += ElementLoaded;
            frameworkElement.Unloaded += ElementUnloaded;
            AttachHandlers();
        }

        public void Detach()
        {
            frameworkElement.Loaded -= ElementLoaded;
            frameworkElement.Unloaded -= ElementUnloaded;
            DetachHandlers();
            handlers.Clear();
            frameworkElement = null;
        }

View on GitHub (pinned to 96fad776d2)