QL-Win/QuickLook · error · PEImageParseException

Optional header not found.

Error message

Optional header not found.

What it means

Immediately after the 20-byte COFF header, the optional header begins with a 2-byte magic number that identifies the image subtype (PE32, PE32+, or ROM). The parser requires at least 2 bytes remaining to read this magic; if fewer bytes remain the optional header does not exist and parsing aborts. The exception Offset marks where the magic should have been.

Source

Thrown at QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/PEImage.cs:112

        DosStub = reader.ReadBytes((int)(DosHeader.PEHeaderOffset - reader.BaseStream.Position));

        // COFF Header
        if (reader.ReadUInt32() != 0x4550) throw new PEImageParseException((int)reader.BaseStream.Position - 4, "COFF header not found.");
        if (reader.BaseStream.Length - reader.BaseStream.Position < 20) throw new PEImageParseException((int)reader.BaseStream.Position, "COFF header incomplete.");

        CoffHeader = new()
        {
            Machine = (ImageMachineType)reader.ReadUInt16(),
            NumberOfSections = reader.ReadUInt16(),
            TimeDateStamp = reader.ReadUInt32(),
            PointerToSymbolTable = reader.ReadUInt32(),
            NumberOfSymbols = reader.ReadUInt32(),
            SizeOfOptionalHeader = reader.ReadUInt16(),
            Characteristics = (ImageCharacteristics)reader.ReadUInt16()
        };

        // Optional Header
        if (reader.BaseStream.Length - reader.BaseStream.Position < 2) throw new PEImageParseException((int)reader.BaseStream.Position, "Optional header not found.");
        ushort magic = reader.ReadUInt16();

        if (magic == 0x10b)
        {
            if (reader.BaseStream.Length - reader.BaseStream.Position < 94) throw new PEImageParseException((int)reader.BaseStream.Position, "Optional header incomplete.");

            OptionalHeader = new ImageOptionalHeader32
            {
                MajorLinkerVersion = reader.ReadByte(),
                MinorLinkerVersion = reader.ReadByte(),
                SizeOfCode = reader.ReadUInt32(),
                SizeOfInitializedData = reader.ReadUInt32(),
                SizeOfUninitializedData = reader.ReadUInt32(),
                AddressOfEntryPoint = reader.ReadUInt32(),
                BaseOfCode = reader.ReadUInt32(),
                BaseOfData = reader.ReadUInt32(),
                ImageBase = reader.ReadUInt32(),
                SectionAlignment = reader.ReadUInt32(),

View on GitHub (pinned to cb5d9c429c)

Solutions

  1. Confirm you are passing a linked PE image (.exe or .dll), not a COFF object file (.obj)
  2. If the file is an image, re-download and verify its integrity via hash comparison
  3. Guard the call with try-catch(PEImageParseException) when processing untrusted or user-selected files
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify SizeOfOptionalHeader > 0 (object files have it set to 0)
static bool HasOptionalHeader(string path)
{
    byte[] b = File.ReadAllBytes(path);
    if (b.Length < 0x40) return false;
    int peOff = BitConverter.ToInt32(b, 0x3C);
    int coffOff = peOff + 4; // after PE signature
    if (coffOff + 20 > b.Length) return false;
    // SizeOfOptionalHeader is at COFF offset + 16 (2 bytes)
    ushort sizeOfOptHeader = BitConverter.ToUInt16(b, coffOff + 16);
    return sizeOfOptHeader > 0;
}

Try / catch

try
{
    var image = PEImage.FromFile(path);
}
catch (PEImageParseException ex) when (ex.Message.Contains("Optional header not found"))
{
    // Likely a .obj object file or truncated image
    logger.Warn($"No optional header: {ex.Message}");
}

Prevention

When it happens

Trigger: The COFF header was parsed successfully but fewer than 2 bytes remain in the stream. This occurs when the file ends immediately after the COFF header, or when the COFF header's SizeOfOptionalHeader field is 0 (typical of COFF object files that are not memory images).

Common situations: Passing a COFF object file (.obj) instead of a linked image (.exe/.dll) — object files have SizeOfOptionalHeader = 0 and no optional header; a truncated download where the file ends at the COFF/optional-header boundary; a stripped or malformed image.

Related errors


AI-assisted analysis of QL-Win/QuickLook@cb5d9c429c (2026-08-13). Data as JSON: /api/errors/715f8920dfdb7bf7. Report an issue: GitHub.