dotnet/wpf · error · InvalidOperationException

ROP data was incorrectly serialized

Error message

ROP data was incorrectly serialized

What it means

Thrown by PersistRasterOperation after writing the RasterOperation value: the number of bytes actually written to the stream differs from ropSize reported by GetDataSizeIfKnownGuid. The serializer keeps the size table in sync with what it writes, so a mismatch means the writer wrote the wrong amount of data for the ROP value — an internal serialization invariant violation.

Solutions

  1. Ensure any RasterOperation entry added to the GuidList declares the correct size (4 bytes for the standard ROP value).
  2. Avoid manual manipulation of the GuidList / ISF internals; use the public StrokeCollection.Save API.
  3. Reset DrawingAttributes to standard values (default RasterOperation) before saving to skip the ROP path entirely.
  4. Catch InvalidOperationException around Save and re-throw with the drawing attributes for diagnostics.

Example fix

// before: custom size registered wrongly
guidList.Add(KnownIds.RasterOperation, size: 2);
// after: standard ROP value is 4 bytes
guidList.Add(KnownIds.RasterOperation, size: 4);
Defensive patterns

Strategy: try-catch

Validate before calling

Debug.Assert(guidList.GetDataSizeIfKnownGuid(KnownIds.RasterOperation) == 4, "RasterOperation data size must be 4 bytes");

Type guard

bool HasValidRopSize(GuidList g) => g.GetDataSizeIfKnownGuid(KnownIds.RasterOperation) == 4;

Try / catch

try { collection.Save(stream); }
catch (InvalidOperationException ex) when (ex.Message.Contains("ROP data was incorrectly serialized")) { /* regenerate GuidList or reset attributes */ }

Prevention

When it happens

Trigger: EncodeAsISF on DrawingAttributes whose RasterOperation value serializes to a byte count different from the size the GuidList associates with the RasterOperation GUID — typically caused by corrupt/modified GuidList state or custom ROP data inserted with a wrong size.

Common situations: Custom ISF code that registered a RasterOperation data size inconsistent with the 4-byte value the serializer writes; debugging/patching internals of the ink serialization stack.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Ink/InkSerializedFormat/DrawingAttributeSerializer.cs:541

        private static void PersistRasterOperation(DrawingAttributes da, Stream stream, GuidList guidList, ref uint cbData, ref BinaryWriter bw)
        {
            // write any non-default RasterOp value that we might have picked up from 
            // V1 interop or by setting IsHighlighter.
            if (da.RasterOperation != DrawingAttributeSerializer.RasterOperationDefaultV1)
            {
                uint ropSize = GuidList.GetDataSizeIfKnownGuid(KnownIds.RasterOperation);
                if (ropSize == 0)
                {
                    throw new InvalidOperationException(StrokeCollectionSerializer.ISFDebugMessage("ROP data size was not found"));
                }

                Debug.Assert(bw != null);
                cbData += SerializationHelper.Encode(stream, (uint)guidList.FindTag(KnownIds.RasterOperation, true));
                long currentPosition = stream.Position;
                bw.Write(da.RasterOperation);
                if ((uint)(stream.Position - currentPosition) != ropSize)
                {
                    throw new InvalidOperationException(StrokeCollectionSerializer.ISFDebugMessage("ROP data was incorrectly serialized"));
                }
                cbData += ropSize;
            }
        }
#if OLD_ISF
        /// <Summary>
        /// Encodes the ExtendedProperties in the ISF stream.
        /// </Summary>
#else
        /// <Summary>
        /// Encodes the ExtendedProperties in the ISF stream.
        /// </Summary>
#endif
        private static void PersistExtendedProperties(DrawingAttributes da, Stream stream, GuidList guidList, ref uint cbData, ref BinaryWriter bw, byte compressionAlgorithm, bool fTag)
        {
            // Now save the extended properties
            ExtendedPropertyCollection epcClone = da.CopyPropertyData();

View on GitHub (pinned to 81131a70a4)