dotnet/wpf · error · InvalidOperationException
SR.ResourceDictionaryIsReadOnly
Error message
SR.ResourceDictionaryIsReadOnly
What it means
SetValueWithoutLock throws InvalidOperationException when the dictionary is read-only (IsReadOnly). A dictionary becomes read-only while it is being enumerated inside a deferred-content load or when it is marked immutable (e.g. during a DeferContent load or when sealed by the system), so writes via the indexer are rejected.
Solutions
- Wait until deferred content load completes (e.g. after a Dispatcher pass) before writing to the dictionary.
- Use BeginInit/EndInit around bulk modifications instead of writing during load.
- Clone or create a new ResourceDictionary with the desired values and replace the read-only one in MergedDictionaries.
Example fix
// before
app.Resources["Accent"] = Brushes.Blue; // may hit read-only during load
// after
var rd = new ResourceDictionary { ["Accent"] = Brushes.Blue };
app.Resources.MergedDictionaries.Add(rd); Defensive patterns
Strategy: validation
Validate before calling
if (dictionary.IsReadOnly) throw new InvalidOperationException("Cannot write to a read-only ResourceDictionary; wait for load/enumeration to finish or replace the dictionary"); Type guard
bool CanWrite(ResourceDictionary rd) => !rd.IsReadOnly;
Try / catch
try { dictionary[key] = value; } catch (InvalidOperationException ex) when (ex.Message.Contains("read-only")) { Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => dictionary[key] = value)); } Prevention
- Check IsReadOnly before mutating a shared ResourceDictionary
- Perform all dictionary mutations on the UI thread outside load/enumeration windows
- Prefer replacing a whole merged dictionary over in-place writes on shared dictionaries
When it happens
Trigger: Setting dictionary[key] = value (this[key]) or other write paths while IsReadOnly is true — typically while another thread is enumerating/loading the dictionary's deferred content, or after the dictionary was sealed/invalidated.
Common situations: Theming code writing into a shared Application-level Resources dictionary while a load/enumeration is in flight; modifying dictionaries on frozen/sealed theme resources.
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
- SR.EndInitWithoutBeginInitNotSupported
- SR.ExpectedResourceDictionaryTarget
- SR.FontFamily_ReadOnly
- SR.Format(SR.ResourceDictionaryLoadFromFailure…
- SR.Format(SR.ResourceDictionaryLoadFromFailure, value ==…
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/adf5c0ed2e422486.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/ResourceDictionary.cs:435
// property in this scenario.
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public DeferrableContent DeferrableContent
{
get
{
return null;
}
set
{
this.SetDeferrableContent(value);
}
}
private void SetValueWithoutLock(object key, object value)
{
if (IsReadOnly)
{
throw new InvalidOperationException(SR.ResourceDictionaryIsReadOnly);
}
object oldValue = _baseDictionary[key];
if (oldValue != value)
{
// We need to validate all the deferred references that refer
// to the old resource before we overwrite it.
ValidateDeferredResourceReferences(key);
if( TraceResourceDictionary.IsEnabled )
{
TraceResourceDictionary.Trace( TraceEventType.Start,
TraceResourceDictionary.AddResource,
this,
key,
value );
}View on GitHub (pinned to 81131a70a4)