dotnet/wpf · error · InvalidOperationException

throw new…

Error message

throw new InvalidOperationException(SR.Format(SR.FrugalMap_CannotPromoteBeyondHashtable));

What it means

HashObjectMap is the terminal store in the FrugalMap promotion chain, so its Promote override is unreachable and unconditionally throws InvalidOperationException('Cannot promote beyond Hashtable'). Hitting it means the promotion logic attempted to grow an already-maximal store.

Solutions

  1. Never call Promote on a HashObjectMap — it is the largest store tier
  2. Check FrugalMapStoreState before choosing a promotion target
  3. If thrown inside stock WPF, file a bug with the call stack

Example fix

// before
hashMapStore.Promote(nextStore); // always throws
// after
if (hashMapStore is not HashObjectMap) hashMapStore.Promote(nextStore);
Defensive patterns

Strategy: type-guard

Validate before calling

if (store is HashObjectMap) { /* terminal store: do not promote */ return; }

Type guard

bool IsTerminalStore(FrugalMapBase store) => store is HashObjectMap;

Try / catch

try { store.Promote(next); } catch (InvalidOperationException ex) when (ex.Message.Contains("Hashtable")) { /* already at max tier */ }

Prevention

When it happens

Trigger: Calling Promote directly on a HashObjectMap store, or internal code paths that select HashObjectMap as a promotion target instead of the terminal store.

Common situations: Only during custom storage experiments or WPF framework bugs; application code never calls store Promote directly since FrugalMap.InsertEntry manages store selection.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/03efb64b5a940455. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/Shared/MS/Utility/FrugalMap.cs:1640

            }
        }

        public override void Iterate(ArrayList list, FrugalMapIterationCallback callback)
        {
            foreach (KeyValuePair<int, object> entry in _entries)
            {
                object value = (entry.Value != NullValue) ?
                    entry.Value :
                    DependencyProperty.UnsetValue;
            
                callback(list, entry.Key, value);
            }
        }

        public override void Promote(FrugalMapBase newMap)
        {
            // Should never get here
            throw new InvalidOperationException(SR.Format(SR.FrugalMap_CannotPromoteBeyondHashtable));
        }

        // Size of this data store
        public override int Count
        {
            get
            {
                return _entries.Count;
            }
        }

        // 163 is chosen because it is the first prime larger than 128, the MAXSIZE of SortedObjectMap
        internal const int MINSIZE = 163;

        // Hashtable will return null from its indexer if the key is not
        // found OR if the value is null.  To distinguish between these
        // two cases we insert NullValue instead of null.
        private static object NullValue = new object();

View on GitHub (pinned to 81131a70a4)