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

  1. Wait until deferred content load completes (e.g. after a Dispatcher pass) before writing to the dictionary.
  2. Use BeginInit/EndInit around bulk modifications instead of writing during load.
  3. 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

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


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)