t8y2/dbx · error · PeFormatError
truncated PE import descriptor
Error message
truncated PE import descriptor
What it means
Each IMAGE_IMPORT_DESCRIPTOR is a 20-byte structure walked sequentially from the import directory. This error is raised when fewer than 20 bytes remain in the file at the current descriptor offset, i.e. the import descriptor array runs off the end of the file before the terminating all-zero descriptor is found. It indicates a truncated or corrupt import table rather than a legitimate terminator.
Source
Thrown at agents/scripts/validate_windows_pe_dependencies.py:91
)
def rva_to_offset(rva: int) -> int:
if rva < size_of_headers:
return rva
for virtual_address, virtual_size, raw_offset, raw_size in sections:
span = max(virtual_size, raw_size)
if virtual_address <= rva < virtual_address + span:
delta = rva - virtual_address
if delta >= raw_size:
break
return raw_offset + delta
raise PeFormatError(f"PE RVA 0x{rva:x} is not backed by file data")
descriptor_offset = rva_to_offset(import_directory_rva)
imports = []
for _ in range(4096):
if descriptor_offset + 20 > len(data):
raise PeFormatError("truncated PE import descriptor")
descriptor = data[descriptor_offset : descriptor_offset + 20]
if descriptor == b"\0" * 20:
return sorted(set(imports), key=str.casefold)
name_rva = _read_u32(data, descriptor_offset + 12)
if name_rva == 0:
raise PeFormatError("PE import descriptor has no DLL name")
imports.append(_read_c_string(data, rva_to_offset(name_rva)))
descriptor_offset += 20
raise PeFormatError("PE import descriptor table is not terminated")
def forbidden_msvc_runtime_dlls(imports: list[str]) -> list[str]:
return sorted(
{name for name in imports if name.casefold().startswith(("msvcp", "vcruntime"))},
key=str.casefold,
)
View on GitHub (pinned to c0390bff16)
Solutions
- Re-download or rebuild the binary and compare its size/hash against the original artifact; truncation is the usual cause.
- Validate the import directory RVA/size against the section bounds with pefile before running the script.
- Wrap the call in try/except PeFormatError and report the binary as malformed instead of aborting the whole validation batch.
- If you produce the binaries, check that your linker/packager emits a complete, zero-terminated import descriptor table.
Example fix
// before
imports = imported_dlls(Path(binary_path))
// after
try:
imports = imported_dlls(Path(binary_path))
except PeFormatError as err:
print(f"rejecting {binary_path}: {err}")
sys.exit(1) Defensive patterns
Strategy: try-catch
Validate before calling
import os
def file_size_at_least(path, min_bytes: int) -> bool:
return os.path.getsize(path) >= min_bytes
# Also compare against expected build-artifact size/hash to catch truncation early. Try / catch
try:
imports = imported_dlls(path)
except PeFormatError as err:
if "truncated" in str(err):
quarantine(path, reason=err)
else:
raise Prevention
- Verify downloads/copies with checksums to rule out truncation.
- Don't process binaries mid-transfer or from partial build outputs.
- Validate the import directory size (data dir index 1) is a multiple of 20 before parsing.
- Run pefile.parse_data_directories as a pre-check in CI.
When it happens
Trigger: imported_dlls(path) on a PE where descriptor_offset + 20 exceeds len(data) during the descriptor walk — e.g. the import directory RVA resolves near end-of-file, the file was truncated, or the import directory size field disagrees with the actual descriptor array.
Common situations: Incomplete download or failed copy of the binary; a build artifact sliced by packaging tooling; fuzzed/malicious PE samples with truncated sections; disk corruption on build machines.
Related errors
- unexpected end of PE file
- PE import descriptor has no DLL name
- PE import descriptor table is not terminated
- unsupported PE optional header magic: 0x{optional_magic:04x}
- PE RVA 0x{rva:x} is not backed by file data
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/6b89e2e6e39bcbda.
Report an issue: GitHub.