dotnet/wpf · error · FileFormatException

SR.Format(SR.PublishLicenseStreamHeaderTooLong, headerLen…

Error message

SR.Format(SR.PublishLicenseStreamHeaderTooLong, headerLen, MaxPublishLicenseHeaderLen)

What it means

LoadPublishLicense enforces a maximum publish-license header size (MaxPublishLicenseHeaderLen). If the header length Int32 read from the instance data stream exceeds that bound, the header is considered implausible/malicious or from an incompatible future format, and FileFormatException(SR.Format(SR.PublishLicenseStreamHeaderTooLong, headerLen, MaxPublishLicenseHeaderLen)) is thrown with the offending and maximum lengths.

Solutions

  1. Open the document with a library/runtime version at least as new as the one that wrote it, so longer headers are understood.
  2. Inspect the first Int32 of the RM instance data stream to confirm the header length; if implausibly large, the file is corrupt — restore from backup.
  3. Re-publish the document from the source application with a compatible RM stack to regenerate a standard header.
  4. Catch FileFormatException and report the header-too-long condition (the message includes actual vs max length) to diagnostics.
  5. Do not attempt to patch the length field manually; regenerate the protected document instead.

Example fix

// before: no length sanity check on untrusted data
int headerLen = binaryReader.ReadInt32();
ReadBytes(binaryReader, headerLen);

// after: bound-check before allocating/reading
int headerLen = binaryReader.ReadInt32();
if (headerLen > MaxPublishLicenseHeaderLen)
{
    throw new InvalidDataException(
        $"Publish license header too long: {headerLen} > {MaxPublishLicenseHeaderLen}. " +
        "File may be from a newer format or corrupt.");
}
Defensive patterns

Strategy: validation

Validate before calling

// sanity-check the declared header length against a sane maximum
stream.Seek(0, SeekOrigin.Begin);
Span<byte> buf = stackalloc byte[4];
int headerLen = stream.Read(buf) == 4 ? BitConverter.ToInt32(buf) : 0;
if (headerLen > MaxPublishLicenseHeaderLen)
    throw new InvalidDataException($"Header length {headerLen} exceeds max {MaxPublishLicenseHeaderLen}; file from newer format or corrupt.");

Type guard

static bool HeaderLengthInBounds(Stream s, int maxLen)
{
    long pos = s.Position;
    Span<byte> b = stackalloc byte[4];
    int v = s.Read(b) == 4 ? BitConverter.ToInt32(b) : int.MaxValue;
    s.Seek(pos, SeekOrigin.Begin);
    return v >= sizeof(int) && v <= maxLen;
}

Try / catch

try
{
    publishLicense = transform.LoadPublishLicense();
}
catch (FileFormatException ex) when (ex.Message.Contains("too long"))
{
    log.Warn("Publish license header exceeds supported length; try a newer runtime or re-publish the document.", ex);
    throw;
}

Prevention

When it happens

Trigger: Reading a rights-managed document whose instance data stream starts with an Int32 header length greater than MaxPublishLicenseHeaderLen — produced by a newer format version, by corruption of the length field, or by a deliberately crafted file.

Common situations: Opening a document created by a future/newer WPF RM writer with extended headers; a corrupted length field (e.g. pointer bytes read as Int32); fuzzed or malicious files that set an enormous length value; mixing files between incompatible RM implementations.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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

Appendix: source

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

            // 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;
            if (numPublishLicenseHeaderExtraBytes > 0)
            {
                _publishLicenseHeaderExtraBytes = new byte [numPublishLicenseHeaderExtraBytes];
                if (PackagingUtilities.ReliableRead(_publishLicenseStream, _publishLicenseHeaderExtraBytes, 0, numPublishLicenseHeaderExtraBytes)
                        != numPublishLicenseHeaderExtraBytes)

View on GitHub (pinned to 81131a70a4)