dotnet/wpf · error · InvalidOperationException

SR.Format(SR.PropertyIsImmutable, "DataType"…

Error message

SR.Format(SR.PropertyIsImmutable, "DataType", this.GetType().Name)

What it means

Within an initialization block, DataType may be set once; a second assignment to a different value throws InvalidOperationException with the PropertyIsImmutable message. Template keys hash and compare on DataType, so changing it after it is established would corrupt template lookup semantics.

Solutions

  1. Assign DataType exactly once per initialization block.
  2. Guard with 'if (key.DataType == null)' before assigning inside the initialization block.
  3. Instantiate a new TemplateKey for each distinct DataType.

Example fix

// before
key.BeginInit();
key.DataType = typeof(A);
key.DataType = typeof(B); // throws
// after
key.BeginInit();
if (key.DataType == null)
    key.DataType = typeof(B);
key.EndInit();
Defensive patterns

Strategy: type-guard

Validate before calling

if (key.DataType != value)
{
    key.BeginInit();
    key.DataType = value;
    key.EndInit();
}

Type guard

bool CanAssign(TemplateKey key, object value) => key.DataType == null || key.DataType == value;

Try / catch

try { key.DataType = value; }
catch (InvalidOperationException ex) when (ex.Message.Contains("immutable")) { /* skip, already set */ }

Prevention

When it happens

Trigger: Calling BeginInit(), assigning DataType = typeof(A), then assigning DataType = typeof(B) before EndInit().

Common situations: Retry/reconfiguration loops that apply defaults then overwrite with the real value; generic property-setting frameworks that assign every property including already-set ones.

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/7af19e67e74f177b. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/TemplateKey.cs:71

            _initializing = false;
        }

#endregion ISupportInitialize

        /// <summary>
        /// The type for which the template is designed.  This is either
        /// a Type (for object data), or a string (for XML data).  In the latter
        /// case the string denotes the XML tag name.
        /// </summary>
        public object DataType
        {
            get { return _dataType; }
            set
            {
                if (!_initializing)
                    throw new InvalidOperationException(SR.Format(SR.PropertyIsInitializeOnly, "DataType", this.GetType().Name));
                if (_dataType != null && value != _dataType)
                    throw new InvalidOperationException(SR.Format(SR.PropertyIsImmutable, "DataType", this.GetType().Name));

                Exception ex = ValidateDataType(value, "value");
                if (ex != null)
                    throw ex;

                _dataType = value;
            }
        }

        /// <summary> Override of Object.GetHashCode() </summary>
        public override int GetHashCode()
        {
            // note that the hash code can change, but only during intialization
            // and only once (DataType can only be changed once, from null to
            // non-null, and that can only happen during [Begin/End]Init).
            // Technically this is still a violation of the "constant during
            // lifetime" rule, however in practice this is acceptable.  It is
            // very unlikely that someone will put a TemplateKey into a hashtable

View on GitHub (pinned to 81131a70a4)