stride3d/stride · error · InvalidOperationException
A dependency object must be attached in order to unregister…
Error message
A dependency object must be attached in order to unregister a handler.
What it means
DetachHandler requires the watcher to still be attached to a FrameworkElement; if frameworkElement is null it throws InvalidOperationException 'A dependency object must be attached in order to unregister a handler.' Removing a value-change subscription needs the same live DependencyObject that AddValueChanged was called with. Called by UnregisterValueChangedHander and DetachHandlers.
Solutions
- Track attach state and skip per-handler unregister calls once the watcher is detached — Detach/DetachHandlers already performs full cleanup.
- Perform unregistering inside the element's Unloaded handler before calling watcher Detach, or rely solely on the watcher's own detach path.
- Avoid double-detach by guarding with a bool flag or checking watcher attach state.
- Re-attach the watcher to the element before attempting targeted unregister if the element is live again.
Example fix
// before watcher.Detach(); watcher.UnregisterValueChangedHander(prop, OnChanged); // already detached // after watcher.UnregisterValueChangedHander(prop, OnChanged); watcher.Detach();
Defensive patterns
Strategy: validation
Validate before calling
if (watcher.AssociatedObject == null) { /* already detached; nothing to unregister */ return; } Type guard
static bool CanUnregisterFromWatcher(DependencyPropertyWatcher w) => w?.AssociatedObject is not null;
Try / catch
try { watcher.UnregisterValueChangedHander(prop, handler); }
catch (InvalidOperationException ex) when (ex.Message.Contains("must be attached")) { /* watcher already detached; safe to ignore */ } Prevention
- Rely on the watcher's Detach() for full cleanup instead of mixed manual unregister + detach paths.
- Track detach state with a bool flag to avoid double-detach.
- Order teardown: unregister first, then detach.
- Treat element Unloaded as the single cleanup entry point.
When it happens
Trigger: Calling UnregisterValueChangedHander after the watcher was detached (element reference cleared, e.g. by Detach() or after the element unloaded) or before it was ever attached.
Common situations: Double-detach in unload/unloaded handlers; WPF element unloading (page navigation, ItemsControl recycling) followed by explicit per-handler unregister calls; application shutdown paths racing with cleanup code.
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
- A dependency object must be attached in order to register a…
- Can't change the parent of this folder
- A dependency object is already attached to this instance of…
- No handler was previously registered for this dependency…
- The given is not registered as a clipboard listener.
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/e6d963537e98bee0.
Report an issue: GitHub.
Appendix: source
Thrown at sources/presentation/Stride.Core.Presentation.Wpf/Core/DependencyPropertyWatcher.cs:120
{
if (property == null) throw new ArgumentNullException(nameof(property));
if (handler == null) throw new ArgumentNullException(nameof(handler));
if (frameworkElement == null) throw new InvalidOperationException("A dependency object must be attached in order to register a handler.");
DependencyPropertyDescriptor descriptor;
if (!descriptors.TryGetValue(property, out descriptor))
{
descriptor = DependencyPropertyDescriptor.FromProperty(property, AssociatedObject.GetType());
descriptors.Add(property, descriptor);
}
descriptor.AddValueChanged(AssociatedObject, handler);
}
private void DetachHandler([NotNull] DependencyProperty property, [NotNull] EventHandler handler)
{
if (property == null) throw new ArgumentNullException(nameof(property));
if (handler == null) throw new ArgumentNullException(nameof(handler));
if (frameworkElement == null) throw new InvalidOperationException("A dependency object must be attached in order to unregister a handler.");
DependencyPropertyDescriptor descriptor;
if (!descriptors.TryGetValue(property, out descriptor))
{
throw new InvalidOperationException("No handler was previously registered for this dependency property.");
}
descriptor.RemoveValueChanged(AssociatedObject, handler);
}
private void ElementLoaded(object sender, RoutedEventArgs e)
{
AttachHandlers();
}
private void ElementUnloaded(object sender, RoutedEventArgs e)
{
DetachHandlers();
}View on GitHub (pinned to 96fad776d2)