dotnet/wpf · error · System.InvalidOperationException

SR.Verify_ApartmentState (template: Verify_ApartmentState…

Error message

SR.Verify_ApartmentState (template: Verify_ApartmentState, arg: requiredState)

What it means

Verify.IsApartmentState asserts that the calling thread's COM apartment state (STA/MTA/Unknown) matches the state required by the API being called. WindowsBase throws InvalidOperationException because apartment affinity is a caller/threading contract, not an argument problem: the operation simply cannot run on a thread with the wrong apartment state. This is used by WPF APIs that require an STA thread (e.g. for UI or clipboard/ Freezable work).

Solutions

  1. Before starting the thread, set Thread.CurrentThread.SetApartmentState(ApartmentState.STA) (must be called before Thread.Start()).
  2. Move the call onto an STA thread: new Thread(...) { SetApartmentState = ApartmentState.STA } and marshal the result back.
  3. If on the UI thread already, ensure the work runs via Dispatcher.Invoke/BeginInvoke on the STA UI dispatcher instead of a worker thread.
  4. For Task-based code, use a dedicated STA scheduler/Thread rather than Task.Run (ThreadPool threads are MTA).

Example fix

// before
Task.Run(() => CreateVisual()); // MTA thread -> InvalidOperationException
// after
var t = new Thread(() => CreateVisual());
t.SetApartmentState(ApartmentState.STA);
t.Start();
Defensive patterns

Strategy: validation

Validate before calling

if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
    throw new InvalidOperationException("This API requires an STA thread. Use SetApartmentState(ApartmentState.STA) before Thread.Start(), or dispatch to the UI thread.");

Type guard

bool IsStaThread() => Thread.CurrentThread.GetApartmentState() == ApartmentState.STA;

Try / catch

try
{
    CallStaOnlyApi();
}
catch (InvalidOperationException ex) when (ex.Message.Contains("ApartmentState"))
{
    RunOnStaThread(() => CallStaOnlyApi());
}

Prevention

When it happens

Trigger: Calling an API that wraps Verify.IsApartmentState(ApartmentState.STA) (or a specific requiredState) from a thread whose Thread.GetApartmentState() differs - e.g. calling from a plain ThreadPool/Task thread or an MTA thread when STA is required.

Common situations: Creating WPF visuals, Freezables, or using STA-only COM interop from a background Task.Run thread; spawning a Thread without SetApartmentState(ApartmentState.STA) before Start(); hosting WPF in a console/service whose main thread is MTA.

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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/Verify.cs:31

    internal static class Verify
    {
        /// <summary>
        /// Ensure that the current thread's apartment state is what's expected.
        /// </summary>
        /// <param name="requiredState">
        /// The required apartment state for the current thread.
        /// </param>
        /// <param name="message">
        /// The message string for the exception to be thrown if the state is invalid.
        /// </param>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the calling thread's apartment state is not the same as the requiredState.
        /// </exception>
        public static void IsApartmentState(ApartmentState requiredState)
        {
            if (Thread.CurrentThread.GetApartmentState() != requiredState)
            {
                throw new InvalidOperationException(SR.Format(SR.Verify_ApartmentState, requiredState));
            }
        }

        /// <summary>
        /// Ensure that an argument is neither null nor empty.
        /// </summary>
        /// <param name="value">The string to validate.</param>
        /// <param name="name">The name of the parameter that will be presented if an exception is thrown.</param>
        public static void IsNeitherNullNorEmpty(string value, string name)
        {
            // catch caller errors, mixing up the parameters.  Name should never be empty.
            Debug.Assert(!string.IsNullOrEmpty(name));

            // Notice that ArgumentNullException and ArgumentException take the parameters in opposite order :P
            if (value == null)
            {
                throw new ArgumentNullException(name, SR.Verify_NeitherNullNorEmpty);
            }

View on GitHub (pinned to 81131a70a4)