dotnet/wpf · error · FileFormatException

SR.PublishLicenseStreamCorrupt

Error message

SR.PublishLicenseStreamCorrupt

What it means

RightsManagementEncryptionTransform.LoadPublishLicense reads the publish-license instance data stream from a rights-managed compound file. The first Int32 in that stream is the header length, which must at minimum cover itself (Int32Size = 4 bytes). If headerLen < 4, the stream content is not a valid publish-license header and FileFormatException(SR.PublishLicenseStreamCorrupt) is thrown.

Solutions

  1. Treat the file as corrupt: restore from backup or re-obtain the original rights-managed document.
  2. Re-save/re-publish the document from the originating application to rewrite the instance data stream.
  3. Inspect the RM transform's instance data stream in the compound file and confirm the first 4 bytes decode to a sane Int32 header length (>= 4).
  4. Catch FileFormatException during PublishLicense/OpenPackage and show a 'corrupt protected document' error instead of crashing.
  5. If writing such files yourself, ensure SavePublishLicense always emits the Int32 header length before any payload.

Example fix

// before: blindly opening a suspect file
var publishLicense = new PublishLicense(rmStreamData);

// after: validate the length prefix first
int headerLen = ReadInt32AtStart(rmStreamData);
if (headerLen < sizeof(int))
{
    throw new InvalidDataException(
        "Publish license header length is invalid; the protected document is corrupt.");
}
var publishLicense = new PublishLicense(rmStreamData);
Defensive patterns

Strategy: validation

Validate before calling

// peek the header length before attempting to load
stream.Seek(0, SeekOrigin.Begin);
Span<byte> buf = stackalloc byte[4];
if (stream.Read(buf) < 4 || BitConverter.ToInt32(buf) < sizeof(int))
    throw new InvalidDataException("Publish license header length invalid; file is corrupt.");

Type guard

static bool HasValidLicenseHeader(Stream s)
{
    long pos = s.Position;
    Span<byte> b = stackalloc byte[4];
    bool ok = s.Read(b) == 4 && BitConverter.ToInt32(b) >= 4;
    s.Seek(pos, SeekOrigin.Begin);
    return ok;
}

Try / catch

try
{
    publishLicense = transform.LoadPublishLicense();
}
catch (FileFormatException ex)
{
    log.Error("Publish license header corrupt in protected document", ex);
    throw new InvalidDataException("Protected document is corrupt; restore from a good copy.", ex);
}

Prevention

When it happens

Trigger: Opening a rights-managed document whose primary instance data stream contains a header length field less than 4 (0, negative, or garbage bytes) — typically because the stream holds random/corrupt data or was written by a different format.

Common situations: Compound file corrupted by a failed save; a stream that was overwritten or truncated leaving zero bytes that get read as 0 length prefix; opening a file not actually produced by the RM encryption pipeline; byte-order or format mismatch from a hand-crafted file.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/CompoundFile/RightsManagementEncryptionTransform.cs:125

            // does -not- close the underlying stream.
            //

            // The stream is not owned by the BlockManager, therefore we cannot 
            // close the BinaryWriter, as that would Close the stream underneath.
            // TODO: Use leaveOpen ctor
            BinaryReader utf8Reader = new BinaryReader(_publishLicenseStream, Encoding.UTF8);

            //
            // There follows a variable-length header (not to be confused with the physical
            // stream header). This header allows future expansion, in case we want to store
            // something in addition to the publish license in the primary instance data stream
            // for this transform. The first field in the header is the header length in bytes
            // (including the headerLen field itself).
            //
            Int32 headerLen = utf8Reader.ReadInt32();
            if (headerLen < CU.Int32Size)
            {
                throw new FileFormatException(SR.PublishLicenseStreamCorrupt);
            }

            if (headerLen > MaxPublishLicenseHeaderLen)
            {
                throw new FileFormatException(
                                SR.Format(SR.PublishLicenseStreamHeaderTooLong,
                                headerLen,
                                MaxPublishLicenseHeaderLen
                                ));
            }

            //
            // Save any additional bytes in the header that we don't recognize, so we can
            // write them back out later if necessary. We've already read the headerLen field,
            // so subtract the size of that field from the amount we have to save.
            //
            // No need to use checked{} here since we already made sure that header length is greater than Int32Size
            Int32 numPublishLicenseHeaderExtraBytes = headerLen - CU.Int32Size;

View on GitHub (pinned to 81131a70a4)