QL-Win/QuickLook · error · PEImageParseException

COFF header not found.

Error message

COFF header not found.

What it means

The parser reads the 4-byte PE signature at the file offset stored in the DOS header's PEHeaderOffset field (e_lfanew). Per the PE/COFF specification this signature must be 0x4550 (ASCII "PE\0\0"). If the 4 bytes at that offset do not match, the file is not a valid Win32 PE image and parsing aborts. The Offset property of the exception points 4 bytes before the current read position (the start of the signature).

Source

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

            Reserved6 = reader.ReadUInt16(),
            Reserved7 = reader.ReadUInt16(),
            Reserved8 = reader.ReadUInt16(),
            Reserved9 = reader.ReadUInt16(),
            Reserved10 = reader.ReadUInt16(),
            Reserved11 = reader.ReadUInt16(),
            Reserved12 = reader.ReadUInt16(),
            Reserved13 = reader.ReadUInt16(),
            Reserved14 = reader.ReadUInt16(),
            PEHeaderOffset = reader.ReadUInt32()
        };

        // DOS Stub
        if (reader.BaseStream.Length < DosHeader.PEHeaderOffset) throw new PEImageParseException((int)reader.BaseStream.Position, "DOS stub incomplete.");

        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)

View on GitHub (pinned to cb5d9c429c)

Solutions

  1. Confirm the file is a genuine 32-bit or 64-bit Windows PE image using dumpbin /headers or a PE viewer like PE-bear
  2. Verify the file was fully downloaded or copied by comparing its size or hash against the source
  3. If parsing user-selected or untrusted files, pre-validate the MZ + PE signature before calling PEImage.FromFile and skip non-PE files gracefully
  4. Wrap the parse call in a try-catch for PEImageParseException and surface a user-friendly message for non-PE inputs

Example fix

// before
var image = PEImage.FromFile(path); // throws on old DOS EXE

// after
static bool IsValidPE(string path)
{
    byte[] b = File.ReadAllBytes(path);
    if (b.Length < 0x40 || b[0] != 0x4D || b[1] != 0x5A) return false;
    int peOff = BitConverter.ToInt32(b, 0x3C);
    return peOff >= 0 && peOff + 4 <= b.Length
        && b[peOff] == 0x50 && b[peOff + 1] == 0x45
        && b[peOff + 2] == 0x00 && b[peOff + 3] == 0x00;
}

if (!IsValidPE(path)) return;
var image = PEImage.FromFile(path);
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate MZ + PE signature before full parse
static bool IsValidPESignature(string path)
{
    byte[] b = File.ReadAllBytes(path);
    if (b.Length < 0x40) return false;
    if (b[0] != 0x4D || b[1] != 0x5A) return false; // "MZ"
    int peOff = BitConverter.ToInt32(b, 0x3C);      // e_lfanew
    if (peOff < 0 || peOff + 4 > b.Length) return false;
    return b[peOff] == 0x50 && b[peOff + 1] == 0x45  // "PE"
        && b[peOff + 2] == 0x00 && b[peOff + 3] == 0x00;
}

// Usage
if (!IsValidPESignature(path)) { /* not a PE file, skip */ return; }

Try / catch

try
{
    var image = PEImage.FromFile(path);
    // use image.DosHeader, image.CoffHeader, etc.
}
catch (PEImageParseException ex)
{
    // ex.Offset = byte position where parsing failed
    // ex.Message = description of what was expected
    logger.Warn($"Not a valid PE image: {ex.Message} at offset {ex.Offset}");
}

Prevention

When it happens

Trigger: Calling PEImage.FromFile(path) or PEImage.FromBinary(bytes) on a file whose first two bytes are "MZ" (0x5A4D) and whose e_lfanew field correctly points to a location, but the 4 bytes at that location are not 0x4550. Common with legacy 16-bit DOS executables that have no PE header, or with files that only coincidentally start with "MZ".

Common situations: Passing an old DOS .exe that predates the PE format; passing a non-executable file that happens to start with "MZ"; a partially downloaded or corrupted binary where the e_lfanew pointer or the bytes at that offset were damaged; feeding an object file (.obj) or archive that has no PE signature.

Related errors


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