dotnet/wpf · error · XamlSchemaException
SR.ConverterMustDeriveFromBase
Error message
SR.ConverterMustDeriveFromBase
What it means
XamlValueConverter<TConverterBase>.CreateInstance throws XamlSchemaException('ConverterMustDeriveFromBase') when the configured ConverterType does not derive from (implement/inherit) TConverterBase. The generic converter lazily instantiates ConverterType and must cast it to TConverterBase; validating assignability up front produces a clear schema exception instead of an opaque InvalidCastException later.
Solutions
- Make ConverterType derive from/implement TConverterBase (correct the class or the registration).
- Fix the registration so the right base generic is used, e.g. XamlValueConverter<TypeConverter> for TypeConverter-derived classes.
- Pre-check typeof(TConverterBase).IsAssignableFrom(converterType) before creating the XamlValueConverter.
- Catch XamlSchemaException around ConverterInstance access and fall back to a default converter.
Example fix
// before var c = new XamlValueConverter<ValueSerializer>(typeof(MyTypeConverter), targetType, null); // wrong base c.ConverterInstance; // throws // after var c = new XamlValueConverter<TypeConverter>(typeof(MyTypeConverter), targetType, null);
Defensive patterns
Strategy: validation
Validate before calling
if (converterType is not null && !typeof(TConverterBase).IsAssignableFrom(converterType)) throw new InvalidOperationException($"{converterType} must derive from {typeof(TConverterBase)}"); Type guard
static bool IsValidConverter(Type t) => t is not null && typeof(TConverterBase).IsAssignableFrom(t);
Try / catch
try { var inst = converter.ConverterInstance; } catch (XamlSchemaException ex) { /* converter does not derive from base */ } Prevention
- Match the generic parameter to the converter's actual base (TypeConverter vs ValueSerializer).
- Validate registrations with IsAssignableFrom at startup.
- Avoid constructing converters from raw type-name strings without verification.
- Pin converter assemblies so inheritance chains do not silently change across versions.
When it happens
Trigger: Accessing the ConverterInstance property of a XamlValueConverter<TConverterBase> whose ConverterType was set to a class not assignable to TConverterBase — e.g. registering a TypeConverter that does not derive from the expected base (like a ValueSerializer pointing at a TypeConverter, or a generic parameter mismatch when constructing the converter).
Common situations: XAML schema/type-converter registrations where the ConverterType string in configuration resolves to the wrong class; swapping a base-converter generic parameter after a refactor; assembly-version changes where a converter class no longer inherits the expected base.
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
- SR.Format(SR.MustBeOfType, nameof(value)…
- ArgumentException(SR.XamlXmlWriterCannotWriteNonstringValue…
- SR.Animation_ChildMustBeKeyFrame
- SR.Animation_ChildMustBeKeyFrame
- SR.Animation_ChildMustBeKeyFrame
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/2ea139827f99504c.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/Schema/XamlValueConverter.cs:81
_isPublic = (ConverterType is null || ConverterType.IsVisible) ? ThreeValuedBool.True : ThreeValuedBool.False;
}
return _isPublic == ThreeValuedBool.True;
}
}
protected virtual TConverterBase CreateInstance()
{
if (ConverterType == typeof(EnumConverter) &&
TargetType.UnderlyingType is not null && TargetType.UnderlyingType.IsEnum)
{
return (TConverterBase)(object)new EnumConverter(TargetType.UnderlyingType);
}
else if (ConverterType is not null)
{
if (!typeof(TConverterBase).IsAssignableFrom(ConverterType))
{
throw new XamlSchemaException(SR.Format(SR.ConverterMustDeriveFromBase,
ConverterType, typeof(TConverterBase)));
}
return (TConverterBase)Activator.CreateInstance(ConverterType, null);
}
return null;
}
private string GetDefaultName()
{
if (ConverterType is not null)
{
if (TargetType is not null)
{
return $"{ConverterType.Name}({TargetType.Name})";
}
View on GitHub (pinned to 81131a70a4)