PrismLibrary/Prism · error · InvalidOperationException
Resources.EventAggregatorNotConstructedOnUIThread
Error message
Resources.EventAggregatorNotConstructedOnUIThread
What it means
PubSubEvent.Subscribe with ThreadOption.UIThread requires the EventAggregator to have captured a SynchronizationContext at construction. Prism throws InvalidOperationException with Resources.EventAggregatorNotConstructedOnUIThread when SynchronizationContext is null, because it cannot marshal the handler to the UI thread.
Solutions
- Construct the EventAggregator on the UI thread so SynchronizationContext.Current is captured
- Subscribe with ThreadOption.BackgroundThread or PublisherThread if UI marshaling is not required
- Explicitly set SynchronizationContext.Current before constructing the aggregator (e.g. capture the main thread's context)
- Register the EventAggregator via DI on the UI thread during app startup
Example fix
// before (background thread) var aggregator = new EventAggregator(); myEvent.Subscribe(handler, ThreadOption.UIThread); // throws // after Device.BeginInvokeOnMainThread(() => aggregator.GetEvent<MyEvent>().Subscribe(handler, ThreadOption.UIThread));
Defensive patterns
Strategy: try-catch
Validate before calling
if (SynchronizationContext.Current == null)
throw new InvalidOperationException("Subscribe with ThreadOption.UIThread requires a SynchronizationContext; construct the EventAggregator on the UI thread."); Type guard
bool CanSubscribeOnUIThread(EventAggregator ea) => ea.GetType() != null && SynchronizationContext.Current != null;
Try / catch
try
{
myEvent.Subscribe(handler, ThreadOption.UIThread);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("UI"))
{
myEvent.Subscribe(handler, ThreadOption.BackgroundThread); // fallback
} Prevention
- Create the EventAggregator on the main/UI thread during app startup
- Never new-up an EventAggregator inside Task.Run or a worker thread
- In tests, set SynchronizationContext.Current or use BackgroundThread
When it happens
Trigger: Calling event.Subscribe(handler, ThreadOption.UIThread) on an EventAggregator that was constructed on a background thread (or in a context without a SynchronizationContext, e.g. some console/test/worker scenarios).
Common situations: Creating the EventAggregator in a background worker or before the UI framework initializes, unit tests running without a synchronization context, Xamarin/MAUI apps where the aggregator is built in a non-UI bootstrap path.
Understand the failure class
Background: "X is required", "must be set", "cannot be empty": the missing-required-config error family, from Vertex AI project/location to WeChat keys — this error's family across 18 libraries.
Related errors
- NavigationException.UnsupportedMauiCreation
- ArgumentNullException("delegate")
- ArgumentNullException(nameof(eventSubscription))
- ArgumentNullException(nameof(actionReference))
- Resources.InvalidDelegateRerefenceTypeException (formatted…
AI-assisted analysis of PrismLibrary/Prism@358118cd64 (2026-09-15).
Data as JSON: /api/errors/857851ef6956814b.
Report an issue: GitHub.
Appendix: source
Thrown at src/Prism.Events/PubSubEvent.cs:85
/// If not using a WeakReference (<paramref name="keepSubscriberReferenceAlive"/> is <see langword="true" />), the user must explicitly call Unsubscribe for the event when disposing the subscriber in order to avoid memory leaks or unexpected behavior.
/// <para/>
/// The PubSubEvent collection is thread-safe.
/// </remarks>
public virtual SubscriptionToken Subscribe(Action action, ThreadOption threadOption, bool keepSubscriberReferenceAlive)
{
IDelegateReference actionReference = new DelegateReference(action, keepSubscriberReferenceAlive);
EventSubscription subscription;
switch (threadOption)
{
case ThreadOption.PublisherThread:
subscription = new EventSubscription(actionReference);
break;
case ThreadOption.BackgroundThread:
subscription = new BackgroundEventSubscription(actionReference);
break;
case ThreadOption.UIThread:
if (SynchronizationContext == null) throw new InvalidOperationException(Resources.EventAggregatorNotConstructedOnUIThread);
subscription = new DispatcherEventSubscription(actionReference, SynchronizationContext);
break;
default:
subscription = new EventSubscription(actionReference);
break;
}
return InternalSubscribe(subscription);
}
/// <summary>
/// Publishes the <see cref="PubSubEvent"/>.
/// </summary>
public virtual void Publish()
{
InternalPublish();
}
View on GitHub (pinned to 358118cd64)