stride3d/stride · error · InvalidOperationException

The given is not registered as a clipboard listener.

Error message

The given {window} is not registered as a clipboard listener.

What it means

ClipboardMonitor.UnregisterListener throws this InvalidOperationException when the supplied Window has no entry in the static Listeners dictionary, meaning it was never registered as a clipboard listener (or was already unregistered). The guard exists so unregistering is always paired with a prior RegisterListener call.

Solutions

  1. Only call UnregisterListener on windows that were previously passed to RegisterListener
  2. Track registration state with a bool flag and skip unregistering when false
  3. Wrap the call in try-catch for InvalidOperationException during teardown
  4. Use Listeners.ContainsKey(window) to check registration before unregistering

Example fix

// before
ClipboardMonitor.UnregisterListener(this);
// after
if (ClipboardMonitor.IsRegistered(this))
    ClipboardMonitor.UnregisterListener(this);
Defensive patterns

Strategy: try-catch

Validate before calling

if (window == null) throw new ArgumentNullException(nameof(window));
// only unregister if registration was tracked
if (!isRegistered) return;

Type guard

bool IsListenerRegistered(Window w) => w != null && ClipboardMonitor.IsRegistered(w);

Try / catch

try { ClipboardMonitor.UnregisterListener(window); }
catch (InvalidOperationException) { /* never registered - safe to ignore in teardown */ }

Prevention

When it happens

Trigger: Calling ClipboardMonitor.UnregisterListener(window) before calling RegisterListener(window); calling UnregisterListener twice for the same window; the dictionary entry having been cleared elsewhere.

Common situations: WPF shutdown/teardown code paths that unregister clipboard hooks defensively without tracking whether registration succeeded; duplicate window close handlers; a window that was recreated so the old instance was never registered.

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/5b5266e00d3f4702. Report an issue: GitHub.

Appendix: source

Thrown at sources/presentation/Stride.Core.Presentation.Wpf/Interop/ClipboardMonitor.cs:67

                hwndSource.AddHook(WinProc);
                // set the window as a viewer
                hwndNextViewer = NativeHelper.SetClipboardViewer(hwndSource.Handle);
            });
        }

        /// <summary>
        /// Unregisters the given <paramref name="window"/> as a clipboard viewer.
        /// </summary>
        /// <param name="window"></param>
        /// <exception cref="ArgumentNullException">window is <c>null</c></exception>
        /// <exception cref="InvalidOperationException">window was not previously registered.</exception>
        public static void UnregisterListener([NotNull] Window window)
        {
            if (window == null) throw new ArgumentNullException(nameof(window));

            HwndSource hwndSource;
            if (!Listeners.TryGetValue(window, out hwndSource))
                throw new InvalidOperationException($"The given {window} is not registered as a clipboard listener.");

            window.Dispatcher.Invoke(() =>
            {
                // stop processing window messages
                hwndSource.RemoveHook(WinProc);
                // restore the chain
                NativeHelper.ChangeClipboardChain(hwndSource.Handle, hwndNextViewer);
            });
        }

        [CanBeNull]
        private static HwndSource GetHwndSource([NotNull] Window window)
        {
            var handle = new WindowInteropHelper(window).Handle;
            return handle != IntPtr.Zero ? HwndSource.FromHwnd(handle) : null;
        }

        private static void OnClipboardContentChanged(IntPtr hwnd)

View on GitHub (pinned to 96fad776d2)