dotnet/wpf · error · InvalidOperationException

SR.Format(SR.MustBaseOnStyleOfABaseType, _targetType.Name)

Error message

SR.Format(SR.MustBaseOnStyleOfABaseType, _targetType.Name)

What it means

During Seal(), if a Style declares BasedOn, the base style's TargetType must equal this style's DefaultTargetType or be an assignable-from (base of) this style's TargetType. A BasedOn style targeting an unrelated or derived-incompatible type violates the inheritance contract and throws InvalidOperationException.

Solutions

  1. Make the derived style's TargetType match the base style's TargetType (or a subtype of it).
  2. Choose a different base style whose TargetType is a base class of the current style's TargetType.
  3. Remove BasedOn and re-declare the needed setters if no type-compatible base exists.

Example fix

// before
var baseStyle = new Style(typeof(Button));
var derived = new Style(typeof(CheckBox)) { BasedOn = baseStyle }; // throws on Seal
// after
var baseStyle = new Style(typeof(ToggleButton));
var derived = new Style(typeof(CheckBox)) { BasedOn = baseStyle };
Defensive patterns

Strategy: validation

Validate before calling

if (baseStyle != null && derived.TargetType != null &&
    baseStyle.TargetType != derived.TargetType &&
    !baseStyle.TargetType.IsAssignableFrom(derived.TargetType))
    throw new InvalidOperationException("BasedOn style targets an incompatible type.");

Type guard

static bool BasedOnCompatible(Style derived, Style baseStyle) =>
  baseStyle == null || baseStyle.TargetType == derived.TargetType || baseStyle.TargetType.IsAssignableFrom(derived.TargetType);

Try / catch

try { derived.Seal(); }
catch (InvalidOperationException ex) { log.Error("BasedOn TargetType incompatible", ex); }

Prevention

When it happens

Trigger: style.BasedOn = otherStyle where otherStyle.TargetType is neither the same type nor a base type of this style's TargetType (e.g. a CheckBox style based on a Button style).

Common situations: Refactoring control types without updating BasedOn chains; merging resource dictionaries where a style key now points to a style with a different TargetType; typos in {StaticResource} keys resolving to a similarly-named style of another type.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Style.cs:501

            // 99% case - Style is already sealed.
            if (_sealed)
            {
                return;
            }

            // Most parameter checking is done as "upstream" as possible, but some
            //  can't be checked until Style is sealed.
            if (_targetType == null)
            {
                throw new InvalidOperationException(SR.Format(SR.NullPropertyIllegal, "TargetType"));
            }

            if (_basedOn != null)
            {
                if(DefaultTargetType != _basedOn.TargetType &&
                    !_basedOn.TargetType.IsAssignableFrom(_targetType))
                {
                    throw new InvalidOperationException(SR.Format(SR.MustBaseOnStyleOfABaseType, _targetType.Name));
                }
            }

            // Seal setters
            _setters?.Seal();

            // Seal triggers
            _visualTriggers?.Seal();

            // Will throw InvalidOperationException if we find a loop of
            //  BasedOn references.  (A.BasedOn = B, B.BasedOn = C, C.BasedOn = A)
            CheckForCircularBasedOnReferences();

            // Seal BasedOn Style chain
            _basedOn?.Seal();

            // Seal the ResourceDictionary
            _resources?.IsReadOnly = true;

View on GitHub (pinned to 81131a70a4)