dotnet/wpf · error · ThreadStateException
SR.OleServicesContext_ThreadMustBeSTA
Error message
SR.OleServicesContext_ThreadMustBeSTA
What it means
OleServicesContext's DoDragDrop path requires an STA (single-threaded apartment) thread because OLE drag-drop COM interfaces must run on an STA. If the current thread's apartment state is not STA, it throws ThreadStateException(SR.OleServicesContext_ThreadMustBeSTA) before invoking OLE.
Solutions
- Initiate drag-drop on the UI thread (Dispatcher.Invoke/BeginInvoke to marshal to the STA main thread).
- If creating the thread yourself, call thread.SetApartmentState(ApartmentState.STA) before Thread.Start().
- Ensure [STAThread] is present on the application's Main entry point so the main thread is STA.
Example fix
// before Task.Run(() => DragDrop.DoDragDrop(source, data, DragDropEffects.Copy)); // MTA thread -> throws // after Dispatcher.BeginInvoke(() => DragDrop.DoDragDrop(source, data, DragDropEffects.Copy));
Defensive patterns
Strategy: validation
Validate before calling
if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
throw new InvalidOperationException("DoDragDrop must run on an STA thread"); // guard before calling Type guard
static bool IsStaThread() => Thread.CurrentThread.GetApartmentState() == ApartmentState.STA;
Try / catch
try { DragDrop.DoDragDrop(depObj, dataObj, effects); }
catch (ThreadStateException ex) when (ex.Message.Contains("STA")) { /* marshal to UI thread and retry */ } Prevention
- Only initiate drag-drop from the UI thread.
- Keep [STAThread] on Main; never call WPF drag APIs from Task.Run or thread-pool code.
- Call SetApartmentState(ApartmentState.STA) before starting custom threads that touch OLE.
When it happens
Trigger: Calling DragDrop.DoDragDrop (via OleServicesContext.OleDoDragDrop) from a non-STA thread, e.g. a worker/MTA thread that initiates a drag operation.
Common situations: Starting drag-drop from a Task.Run/background thread, console or service code touching WPF drag APIs, or a thread created with default MTA state (Thread.SetApartmentState never called).
Related errors
- ArgumentNullException: handle
- ElementNotAvailableException
- Processing is disabled while the Dispatcher is in this…
- SR.CalendarCollection_MultiThreadedCollectionChangeNotSuppor…
- SR.CalendarCollection_MultiThreadedCollectionChangeNotSuppor…
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/c19768e82e8a98da.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/OleServicesContext.cs:91
/// </summary>
internal static void EnsureThreadState()
{
// Poke the CurrentOleServicesContext to ensure it is created, this will throw if the current thread is not STA.
_ = CurrentOleServicesContext;
}
/// <summary>
/// Call OLE Interop DoDragDrop(). Initiate OLE DragDrop.
/// </summary>
internal void OleDoDragDrop(
IComDataObject dataObject,
UnsafeNativeMethods.IOleDropSource dropSource,
int allowedEffects,
int[] finalEffect)
{
if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
{
throw new ThreadStateException(SR.OleServicesContext_ThreadMustBeSTA);
}
InputManager inputManager = (InputManager)Dispatcher.CurrentDispatcher.InputManager;
inputManager?.InDragDrop = true;
try
{
UnsafeNativeMethods.DoDragDrop(dataObject, dropSource, allowedEffects, finalEffect);
}
finally
{
inputManager?.InDragDrop = false;
}
}
/// <summary>
/// Call OLE Interop RegisterDragDrop()
/// </summary>View on GitHub (pinned to 81131a70a4)