dotnet/wpf · error · InvalidOperationException
Invalid EP in ISF
Error message
Invalid EP in ISF
What it means
Thrown by CustomAttributeSerializer.EncodeAttribute when encoding an ExtendedProperty whose value is of a type that has no ISF serialization rule (the switch over the value's Type fell through to the default case). ISF can only encode known value types (uint/int/byte[], string, etc.); anything else cannot be written to the stream. The library treats this as a programming/data error rather than an environment failure.
Solutions
- Find the extended property whose value type is unsupported and remove it before saving, or convert its value to a supported type (int, uint, string, byte[]).
- Ensure the value's runtime Type exactly matches one handled by the serializer (e.g. box an int as int, not a convertible double).
- If persistence of complex objects is required, serialize the object to a byte[] or string yourself and store that in the extended property.
- Persist in GIF instead of ISF if you need extended properties that ISF cannot encode, or keep custom data outside the ink stream.
Example fix
// before
stroke.ExtendedProperties.Add("http://example.com/mydata", new MyCustomObject());
strokeCollection.Save(stream, PersistenceFormat.InkSerializedFormat);
// after
stroke.ExtendedProperties.Add("http://example.com/mydata", Convert.ToBase64String(SerializeToBytes(myObj)));
strokeCollection.Save(stream, PersistenceFormat.InkSerializedFormat); Defensive patterns
Strategy: validation
Validate before calling
bool IsIsfEncodable(object v) => v is int || v is uint || v is byte || v is byte[] || v is string || v is float || v is double || v is System.Windows.Media.Color;
if (!IsIsfEncodable(value)) throw new ArgumentException($"Value type {value.GetType()} is not ISF-encodable"); Type guard
bool IsIsfEncodable(object v) => v is int or uint or byte or byte[] or string or float or double or System.Windows.Media.Color;
Prevention
- Only store primitive ISF-supported types in ExtendedProperties before ISF saves
- Serialize complex objects to byte[] or string yourself
- Test ink Save/Load round-trips with all extended properties you register
When it happens
Trigger: Calling StrokeCollection/Stroke DrawingAttributes or ExtendedProperties save path (Save/EncodeToStream) with a custom extended property whose value type is not one of the ISF-supported types (e.g. a custom class, DateTime, or double not handled by the encoder).
Common situations: Storing arbitrary user objects in Stroke.ExtendedProperties or DrawingAttributes.ExtendedProperties and then serializing the ink to ISF (InkCanvas Save, StrokeCollection.Save with format ISF); code that worked with GIF persistence breaks when switching to ISF where type support is stricter.
Related errors
- InvalidEpInIsf
- Packed button length not equal to expected length
- Property data must be a non-reference variant compatible…
- Read different size from stream then expected
- ROP data was incorrectly serialized
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/3e7f399becc5f542.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Ink/InkSerializedFormat/CustomAttributeSerializer.cs:292
bw.Write((byte)0);
}
break;
}
case (VarEnum.VT_DECIMAL)://14
{
decimal data = (decimal)value;
bw.Write(data);
break;
}
case (VarEnum.VT_BSTR)://8
{
string data = (string)value;
bw.Write( System.Text.Encoding.Unicode.GetBytes( data ) );
break;
}
default:
{
throw new InvalidOperationException(SR.InvalidEpInIsf);
}
}
}
#if OLD_ISF
/// <summary>
/// Encodes a custom attribute to the ISF stream
/// </summary>
#else
/// <summary>
/// Encodes a custom attribute to the ISF stream
/// </summary>
#endif
internal static uint EncodeAsISF(Guid id, byte[] data, Stream strm, GuidList guidList, byte compressionAlgorithm, bool fTag)
{
uint cbWrite = 0;
uint cbSize = GuidList.GetDataSizeIfKnownGuid(id);
Debug.Assert(strm != null);
View on GitHub (pinned to 81131a70a4)