dotnet/wpf · error · XamlObjectReaderException

SR.ObjectReaderTypeCannotRoundtrip

Error message

SR.ObjectReaderTypeCannotRoundtrip

What it means

XamlObjectReader throws this during round-trip validation when an object's type cannot be represented in XAML at all — the type has no usable constructor/member representation for markup (and is not a nested type, which gets a different message). The reader aborts because writing the graph as XAML would be impossible.

Solutions

  1. Give the type a public parameterless constructor and settable public properties
  2. Add a TypeConverter (with ConvertFrom/ConvertTo) so the value round-trips via attribute syntax
  3. Replace the value with a XAML-representable type before reading
  4. Skip/omit the offending member from the object graph

Example fix

// before: type with only internal ctor, no converter
// after
public class Point2 { public Point2() { } public double X { get; set; } public double Y { get; set; } }
Defensive patterns

Strategy: validation

Validate before calling

bool Roundtrippable(Type t) => t.GetConstructor(Type.EmptyTypes) != null || t.GetConstructors().Any(c => c.GetParameters().Length > 0) && !t.IsNested && t.IsVisible;
if (!Roundtrippable(obj.GetType())) throw new InvalidOperationException("Type cannot roundtrip via XAML");

Type guard

bool CanRoundtrip(Type t) => !t.IsNested && t.IsVisible && (t.GetConstructor(Type.EmptyTypes) != null || t.GetConvertFrom != null); // adjust: use TypeConverter check
// practical guard:
bool HasConverterOrDefaultCtor(Type t) => !t.IsNested && (t.GetConstructor(Type.EmptyTypes) != null || TypeDescriptor.GetConverter(t).CanConvertFrom(typeof(string)));

Try / catch

try { using var r = new XamlObjectReader(obj); ... } catch (XamlObjectReaderException ex) when (ex.Message.Contains("TypeCannotRoundtrip")) { /* substitute or omit value */ }

Prevention

When it happens

Trigger: new XamlObjectReader(obj) where CheckTypeCanRoundtrip determines a XamlType in the graph lacks a valid instantiation path (no default ctor, no matching ctor/arguments, no converter/extension representation).

Common situations: Graphs containing types designed purely for code (e.g. delegate-bearing, generic-closed types without converters, internal-only types); third-party objects not designed for markup round-tripping; types without TypeConverters used in collections.

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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlObjectReader.cs:1730

                {
                    foreach (var property in objInfo.Properties)
                    {
                        if (((MemberMarkupInfo)property).IsFactoryMethod && !xamlType.UnderlyingType.IsNested)
                        {
                            // this is the case when the class has no public constructor we can use but contains a factory method
                            // and the class is not nested

                            return;
                        }
                    }

                    if (xamlType.UnderlyingType.IsNested)
                    {
                        throw new XamlObjectReaderException(SR.Format(SR.ObjectReaderTypeIsNested, xamlType.Name));
                    }
                    else
                    {
                        throw new XamlObjectReaderException(SR.Format(SR.ObjectReaderTypeCannotRoundtrip, xamlType.Name));
                    }
                }
            }

            public void AssignName(SerializerContext context)
            {
                if (Name is null)
                {
                    Name = context.AllocateIdentifier();
                    AddNameProperty(context);
                }
            }

            public void AssignName(string name, SerializerContext context)
            {
                if (Name is null)
                {
                    Name = name;

View on GitHub (pinned to 81131a70a4)