dotnet/wpf · error

SR.Format(SR.UnserializableKeyValue…

Error message

SR.Format(SR.UnserializableKeyValue, property.Value.GetType().FullName)

What it means

MarkupWriter.WriteItem throws this InvalidOperationException (SR.UnserializableKeyValue with the property value's full type name) when a MarkupObject for a keyed item (e.g. an entry in a dictionary being serialized) exposes its key as an IsKey property. XAML serialization of keys is not implemented by this writer (the <x:Key> format was never added), so any keyed item is unserializable and the exception reports the key value's type.

Solutions

  1. Avoid serializing keyed dictionaries with XamlWriter.Save; write a custom serializer that emits entries explicitly.
  2. Flatten the dictionary into a list of key/value wrapper objects (non-keyed) before saving.
  3. Register a TypeConverter/ValueSerializer for the key type so items are expressed without an IsKey property.

Example fix

// before
var dict = new Dictionary<string, MyData>();
XamlWriter.Save(dict); // InvalidOperationException: UnserializableKeyValue
// after
var list = dict.Select(kv => new Entry { Key = kv.Key, Value = kv.Value }).ToList();
XamlWriter.Save(list); // public non-generic wrapper entries serialize fine
Defensive patterns

Strategy: try-catch

Validate before calling

bool HasKeyedItems(object o) =>
    MarkupWriter.GetMarkupObjectFor(o).Properties.Any(p => p.IsKey);

Try / catch

try { XamlWriter.Save(obj, stream); }
catch (InvalidOperationException ex) when (ex.Message.Contains("key"))
{
    // fall back to serializing entries as key/value wrapper objects
}

Prevention

When it happens

Trigger: Calling XamlWriter.Save / MarkupWriter.WriteItem on a dictionary or keyed collection whose MarkupObject items expose IsKey properties - e.g. serializing a ResourceDictionary-like structure with entries the writer cannot emit.

Common situations: Serializing dictionaries of custom key types; saving resource dictionaries containing non-serializable entries; automating XAML round-trip of collections.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Primitives/MarkupWriter.cs:693

                    bool propertyTagWritten = false;
                    bool explicitTagWritten = false;

                    foreach (MarkupObject subItem in property.Items)
                    {
                        if (!propertyTagWritten)
                        {
                            propertyTagWritten = true;
                            // uri is made addressable above so it is not necessary here
                            if (property.IsAttached || property.PropertyDescriptor == null)
                            {
                                Debug.Assert(!property.IsValueAsString, "Problem with MarkupObject implementation: String values cannnot be composite");

                                if (property.IsKey)
                                {
                                    // When the reader supports <x:Key> ... </x:Key> format do the following:
                                    //   _writer.WriteStartElement("Key", NamespaceCache.XamlNamespace);
                                    // The writer generates an exception for now:
                                    throw new InvalidOperationException(SR.Format(SR.UnserializableKeyValue, property.Value.GetType().FullName));
                                }
                                else
                                {
                                    string dpUri = scope.MakeAddressable(property.DependencyProperty.OwnerType);
                                    WritePropertyStart(scope.GetPrefixOf(dpUri),
                                        $"{property.DependencyProperty.OwnerType.Name}.{property.DependencyProperty.Name}", dpUri);
                                }
                            }
                            else
                            {
                                WritePropertyStart(prefix, $"{item.ObjectType.Name}.{property.PropertyDescriptor.Name}", uri);

                                writtenAttributes[property.Name] = property.Name;
                            }

                            explicitTagWritten = NeedToWriteExplicitTag(property, subItem);

                            if (explicitTagWritten)

View on GitHub (pinned to 81131a70a4)