dotnet/wpf · error · InvalidOperationException

SR.Automation_RecursivePublicCall

Error message

SR.Automation_RecursivePublicCall

What it means

When the SynchronizedInputAdaptor's owner is a UIElement, StartListening forwards to UIElement.StartListeningSynchronizedInput. If that returns false the element is already processing a synchronized-input session (a recursive public automation call is in progress), so the adaptor throws InvalidOperationException with SR.Automation_RecursivePublicCall.

Solutions

  1. Ensure each StartListening is paired with CancelListening/StopListening before starting a new session.
  2. Guard client code against calling StartListening twice concurrently on the same element.
  3. Track session state (bool flag) around synchronized-input usage and serialize calls.
  4. In the app, avoid raising automation events from inside synchronized-input event handlers that could re-enter StartListening.

Example fix

// before
provider.StartListening(type);
provider.StartListening(type); // throws: session already active
// after
provider.StartListening(type);
try { /* consume InputReachedTarget/Other/Discarded */ }
finally { provider.CancelListening(); }
Defensive patterns

Strategy: try-catch

Validate before calling

// track session state to avoid overlapping StartListening calls
if (synchronizedInputSessionActive) throw new InvalidOperationException("session already active");

Type guard

bool CanStartSession(bool sessionActive) => !sessionActive;

Try / catch

try
{
    provider.StartListening(type);
    synchronizedInputSessionActive = true;
}
catch (InvalidOperationException)
{
    provider.CancelListening(); // end stale session, then retry
    provider.StartListening(type);
}

Prevention

When it happens

Trigger: Calling StartListening on a UIElement whose synchronized-input tracking indicates an active session — i.e. StartListening (or a re-entrant automation call) was invoked while a previous Start/CancelListening session was still open on the same element.

Common situations: Re-entrant automation calls during an active synchronized-input session; a UIA client issuing StartListening twice without CancelListening or StopListening in between; nested provider calls triggered from within input event handling.

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 dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/490671364d53562a. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Automation/SynchronizedInputAdaptor.cs:46

        /// <param name="inputType"> Synchronized input type</param>
        void ISynchronizedInputProvider.StartListening(SynchronizedInputType inputType)
        {
            if (inputType != SynchronizedInputType.KeyDown &&
                inputType != SynchronizedInputType.KeyUp &&
                inputType != SynchronizedInputType.MouseLeftButtonDown &&
                inputType != SynchronizedInputType.MouseLeftButtonUp &&
                inputType != SynchronizedInputType.MouseRightButtonDown &&
                inputType != SynchronizedInputType.MouseRightButtonUp)
            {
                throw new ArgumentException(SR.Format(SR.Automation_InvalidSynchronizedInputType, inputType));
            }
            
            UIElement e = _owner as UIElement;
            if (e != null)
            {
                if (!e.StartListeningSynchronizedInput(inputType))
                {
                    throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
                }
            }
            else
            {
                ContentElement ce = _owner as ContentElement;
                if (ce != null)
                {
                    if (!ce.StartListeningSynchronizedInput(inputType))
                    {
                        throw new InvalidOperationException(SR.Automation_RecursivePublicCall);
                    }
                }
                else
                {
                    UIElement3D e3D = (UIElement3D)_owner;
                    if (!e3D.StartListeningSynchronizedInput(inputType))
                    {
                        throw new InvalidOperationException(SR.Automation_RecursivePublicCall);

View on GitHub (pinned to 81131a70a4)