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
- Confirm the file is a genuine 32-bit or 64-bit Windows PE image using dumpbin /headers or a PE viewer like PE-bear
- Verify the file was fully downloaded or copied by comparing its size or hash against the source
- If parsing user-selected or untrusted files, pre-validate the MZ + PE signature before calling PEImage.FromFile and skip non-PE files gracefully
- 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
- Always validate the MZ and PE signatures before calling PEImage.FromFile on untrusted input
- When building a file-preview feature, filter file extensions or signatures so only genuine PE images reach the parser
- Never assume a file with an .exe extension is a valid PE — verify the binary signature
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
- Optional header magic value of '0x{magic:x4}' unknown.
- Data directories incomplete.
- Section headers incomplete.
- COFF header incomplete.
- Optional header not found.
AI-assisted analysis of QL-Win/QuickLook@cb5d9c429c (2026-08-13).
Data as JSON: /api/errors/95e0f4b657fa1b86.
Report an issue: GitHub.