dotnet/wpf · error · InvalidOperationException
Property data must be a non-reference variant compatible…
Error message
Property data must be a non-reference variant compatible type.
What it means
When cloning (copying) an ExtendedProperty, Clone validates that the stored value is of a type it can deep-copy (string, primitive types, arrays of them, etc.). If the value's runtime type is not a non-reference, variant-compatible type the copy falls through to `throw new InvalidOperationException(SR.InvalidDataTypeForExtendedProperty)` — 'Property data must be a non-reference variant compatible type.'
Solutions
- Store only serializable, variant-compatible values: strings, primitives (int, double, bool, DateTime, etc.) and arrays of these.
- Convert custom objects to a supported representation (e.g. byte[] or string, such as XML/JSON) before adding as an extended property.
- Wrap the clone operation in try/catch for InvalidOperationException and fall back to re-attaching properties manually.
- Validate value types when adding property data so invalid data never enters the collection.
Example fix
// before
stroke.AddPropertyData(myCustomId, new MyMetadata { X = 1 }); // clone later throws
// after
stroke.AddPropertyData(myCustomId, Encoding.UTF8.GetBytes(JsonSerializer.Serialize(metadata))); Defensive patterns
Strategy: validation
Validate before calling
static bool IsCloneablePropertyValue(object v) =>
v == null || v is string || v is byte[] ||
v is int || v is long || v is short || v is byte ||
v is double || v is float || v is bool || v is char ||
v is DateTime || v is decimal || v is Guid; Type guard
static bool IsReferenceTypeValue<T>(object v) => v is T && !(v is string || v is byte[]);
Try / catch
try { var clone = original.Clone(); }
catch (InvalidOperationException) { /* re-attach property data manually */ } Prevention
- Store only primitives/strings/byte[] as extended property values
- Serialize custom objects to byte[] or JSON before attaching
- Test clone/serialize round-trips for every property type you use
When it happens
Trigger: Storing a custom reference type (e.g. a class instance, List<T>, or arbitrary object) as an ExtendedProperty value and then cloning the owning object — e.g. cloning a Stroke or ContextNode that carries the property, or round-tripping through ink serialization APIs that clone properties.
Common situations: Attaching rich business objects to ink strokes via AddPropertyData; data persisted by another app version with an unexpected value type; corrupted or hand-edited ISF payloads whose property data doesn't match the declared 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
- GUID cannot be empty.
- SR.EndHitTestingCalled
- SR.GestureRecognizerNotAvailable
- SR.InvalidValueType (formatted with typeof(Matrix))
- ' ' is not a Visual or Visual3D.
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/94f2dc5bdf0c2fcd.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Ink/ExtendedProperty.cs:204
}
else if (type.IsArray)
{
Type elementType = type.GetElementType();
if (elementType.IsValueType && type.GetArrayRank() == 1)
{
//
// copy the array memebers, which we know are copy
// on assignment value types
//
Array newArray = Array.CreateInstance(elementType, ((Array)_value).Length);
Array.Copy((Array)_value, newArray, ((Array)_value).Length);
return new ExtendedProperty(guid, newArray);
}
}
//
// we didn't find a type we expect, throw
//
throw new InvalidOperationException(SR.InvalidDataTypeForExtendedProperty);
}
private Guid _id; // id of attribute
private object _value; // data in attribute
}
}
View on GitHub (pinned to 81131a70a4)