dotnet/wpf · error · ArgumentException
Property data must be a non-reference variant compatible…
Error message
Property data must be a non-reference variant compatible type.
What it means
Thrown by SerializationHelper.ConvertToVarEnum (with throwOnError=true) when an ExtendedProperty value's type has no compatible non-reference VARIANT representation. ISF extended properties must be storable as a by-value VarEnum; types like objects, unknown COM references, or unsupported managed types cannot be serialized into ISF.
Solutions
- Store only VARIANT-compatible types in extended properties: numeric primitives, bool, string, DateTime, byte[], and safe arrays of these
- Convert custom objects to a byte[] or string representation before adding as an extended property
- Catch ArgumentException around save/serialization and remove or convert offending ExtendedProperty keys
- If the value type is legitimately unsupported (ConvertToVarEnum returned VT_UNKNOWN when throwOnError=false), skip persisting that property
Example fix
// before
stroke.AddPropertyData(myGuid, new MyCustomClass { X = 1 }); // throws on ISF save
// after
stroke.AddPropertyData(myGuid, BitConverter.GetBytes(myValue)); // store as byte[]
// or serialize the object to a string/byte[] yourself:
stroke.AddPropertyData(myGuid, Encoding.UTF8.GetBytes(JsonSerializer.Serialize(obj))); Defensive patterns
Strategy: type-guard
Validate before calling
static readonly HashSet<Type> VariantCompatible = new() {
typeof(int), typeof(uint), typeof(short), typeof(ushort), typeof(long), typeof(ulong),
typeof(float), typeof(double), typeof(decimal), typeof(bool), typeof(string),
typeof(DateTime), typeof(byte[])
};
static bool IsExtendedPropertyStorable(object v) =>
v != null && (VariantCompatible.Contains(v.GetType()) ||
(v is Array a && a.Length > 0 && VariantCompatible.Contains(a.GetType().GetElementType()))); Type guard
bool IsStorableExtendedProperty(object value) => value is int or uint or short or ushort or long or ulong or float or double or decimal or bool or string or DateTime or byte[];
Try / catch
try { scs.Save(stream); }
catch (ArgumentException ex) when (ex.Message.Contains("variant")) {
log.Warn("Unsupported extended property type", ex);
// remove offending ExtendedProperty and retry save
} Prevention
- Only add primitive/string/byte[] values via AddPropertyData
- Serialize custom objects to byte[] or string yourself before storing
- Test round-trip (save+load) of strokes with extended properties in CI
When it happens
Trigger: Adding a Stroke/StrokeCollection/StylusPoint Description extended property whose value is not a primitive (e.g. a custom class, array of unsupported element type, or object) and then serializing to ISF via StrokeCollectionSerializer.Save / ExtendedPropertySerializer.EncodeAsISF.
Common situations: Developers storing arbitrary .NET objects in Stroke extended properties and later saving to ISF; passing byte[][] or nested collections; upgrading code that previously used XML persistence to ISF persistence.
Related errors
- Global Custom Attribute tag embedded in ISF stream does not…
- Invalid EP in ISF
- InvalidValueType
- Packed button length not equal to expected length
- Read different size from stream then expected
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/e02b25b4cedef0de.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Ink/InkSerializedFormat/SerializationHelper.cs:322
return (VarEnum.VT_ARRAY | VarEnum.VT_BOOL);
}
else if (typeof(String) == type)
{
return VarEnum.VT_BSTR;
}
else if (typeof(Decimal) == type)
{
return VarEnum.VT_DECIMAL;
}
else if (typeof(Decimal[]) == type)
{
return (VarEnum.VT_ARRAY | VarEnum.VT_DECIMAL);
}
else
{
if (throwOnError)
{
throw new ArgumentException(SR.InvalidDataTypeForExtendedProperty);
}
else
{
return VarEnum.VT_UNKNOWN;
}
}
}
}
}
View on GitHub (pinned to 81131a70a4)