dotnet/wpf · error · InvalidOperationException

SR.Format(SR.TooManyDependencyProperties, ownerType.Name +…

Error message

SR.Format(SR.TooManyDependencyProperties, ownerType.Name + "." + name)

What it means

GetUniqueGlobalIndex throws this InvalidOperationException when GlobalIndexCount reaches the limit of GlobalIndexMask (15 bits, ~32767), meaning more dependency properties have been registered than the per-process global index can represent. The message includes ownerType.name for the property that pushed the count over the limit.

Solutions

  1. Stop registering DPs dynamically in loops; register a fixed set of DPs and store per-item data in regular CLR properties, attached-object dictionaries, or Freezable instances
  2. Replace per-column DPs with a single attached property plus a value lookup keyed by column id
  3. Recycle/reuse already-registered DPs instead of minting new ones per entity type

Example fix

// before
foreach (var col in schema.Columns)
    cols.Add(DependencyProperty.Register(col.Name, typeof(object), typeof(Row))); // unbounded
// after
public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register("Value", typeof(object), typeof(Row));
// one DP; per-column values kept in Row's own dictionary keyed by column
Defensive patterns

Strategy: validation

Validate before calling

// bounded registration check before minting DPs in bulk
if (registeredDpCount + newCount > 32000)
    throw new InvalidOperationException("Approaching DependencyProperty global index limit");

Try / catch

try { var dp = DependencyProperty.Register(name, typeof(object), ownerType); }
catch (InvalidOperationException) { /* index exhausted: switch to attached property / dictionary storage */ }

Prevention

When it happens

Trigger: Registering more than GlobalIndexMask dependency properties in one AppDomain — e.g. dynamic registration in a loop (Register/AddOwner) or an extremely large number of libraries/static DPs.

Common situations: Apps or plugin hosts that register DPs per data column or per config item in unbounded loops; massive dynamic code generation frameworks creating a DP per schema field; long-running plugin systems accumulating registrations.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/DependencyProperty.cs:1100

            {
                packedData |= Flags.IsStringType;
            }

            _packedData = packedData;
        }

        // Synchronized: Covered by DependencyProperty.Synchronized
        internal static int GetUniqueGlobalIndex(Type ownerType, string name)
        {
            // Prevent GlobalIndex from overflow. DependencyProperties are meant to be static members and are to be registered
            // only via static constructors. However there is no cheap way of ensuring this, without having to do a stack walk. Hence
            // concievably people could register DependencyProperties via instance methods and therefore cause the GlobalIndex to
            // overflow. This check will explicitly catch this error, instead of silently malfuntioning.
            if (GlobalIndexCount >= (int)Flags.GlobalIndexMask)
            {
                if (ownerType != null)
                {
                    throw new InvalidOperationException(SR.Format(SR.TooManyDependencyProperties, ownerType.Name + "." + name));
                }
                else
                {
                    throw new InvalidOperationException(SR.Format(SR.TooManyDependencyProperties, "ConstantProperty"));
                }
            }

            // Covered by Synchronized by caller
            return GlobalIndexCount++;
        }

        /// <summary>
        /// This is the callback designers use to participate in the computation of property
        /// values at design time. Eg. Even if the author sets Visibility to Hidden, the designer
        /// wants to coerce the value to Visible at design time so that the element doesn't
        /// disappear from the design surface.
        /// </summary>
        internal CoerceValueCallback DesignerCoerceValueCallback

View on GitHub (pinned to 81131a70a4)