prestodb/presto · critical · PrestoException

DRUID_SEGMENT_LOAD_ERROR

DRUID_SEGMENT_LOAD_ERROR

Error message

Malformed Central Directory File Header; does not start with %08x

What it means

CentralDirectoryFileHeader.read verifies each central directory entry starts with the signature 0x02014b50. When the bytes at the expected entry offset do not match, the central directory is out of sync with the actual file layout, and DRUID_SEGMENT_LOAD_ERROR is thrown.

Source

Thrown at presto-druid/src/main/java/com/facebook/presto/druid/zip/CentralDirectoryFileHeader.java:57

    static final int INTERNAL_ATTRIBUTES_OFFSET = 36;
    static final int EXTERNAL_ATTRIBUTES_OFFSET = 38;
    static final int LOCAL_HEADER_OFFSET_OFFSET = 42;

    private CentralDirectoryFileHeader()
    {
    }

    // read one ZipFileEntry and add into ZipFileData
    public static long read(ZipFileData fileData, DataInputSource dataInputSource, long fileOffset, Charset charset)
            throws IOException
    {
        long position = fileOffset;

        byte[] fixedSizeData = new byte[FIXED_DATA_SIZE];
        dataInputSource.readFully(position, fixedSizeData);
        position += fixedSizeData.length;
        if (!ZipUtil.arrayStartsWith(fixedSizeData, ZipUtil.intToLittleEndian(SIGNATURE))) {
            throw new PrestoException(DRUID_SEGMENT_LOAD_ERROR, String.format("Malformed Central Directory File Header; does not start with %08x", SIGNATURE));
        }

        byte[] name = new byte[ZipUtil.getUnsignedShort(fixedSizeData, FILENAME_LENGTH_OFFSET)];
        byte[] extraField = new byte[ZipUtil.getUnsignedShort(fixedSizeData, EXTRA_FIELD_LENGTH_OFFSET)];
        byte[] comment = new byte[ZipUtil.getUnsignedShort(fixedSizeData, COMMENT_LENGTH_OFFSET)];

        if (name.length > 0) {
            dataInputSource.readFully(position, name);
            position += name.length;
        }
        if (extraField.length > 0) {
            dataInputSource.readFully(position, extraField);
            position += extraField.length;
        }
        if (comment.length > 0) {
            dataInputSource.readFully(position, comment);
            position += extraField.length;
        }

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Re-upload/re-ingest the segment zip; the archive's central directory is corrupt.
  2. Test the archive with `unzip -t` to confirm the corruption, and check the producing pipeline's writer version.
  3. Ensure the file is byte-identical to what was uploaded (compare checksums between producer and deep storage).
  4. If the zip was repaired by a third-party tool, rebuild it with a standard tool (zip/jar) instead.

Example fix

// before: blind retry on corrupt segment
segmentLoader.load(location);
// after: validate archive, then fall back to re-fetch
if (!zipLooksValid(location)) {
    location = reIngestSegment(segmentId);
}
segmentLoader.load(location);
Defensive patterns

Strategy: try-catch

Try / catch

try {
    centralDirectoryHeaders = readHeaders(source, eocd);
} catch (PrestoException e) {
    if (String.valueOf(e.getMessage()).contains("Malformed Central Directory File Header")) {
        handleCorruptSegment(segmentId);
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: read() called by bytesRead for an entry at fileOffset whose fixed-size data does not begin with SIGNATURE — i.e., the offset/count derived from the EOCD is wrong or the directory region is corrupted.

Common situations: Corrupted/truncated zips where the EOCD points at the wrong central directory offset (wrong offsets stored by a buggy writer), files modified in place after upload, or non-zip data whose tail coincidentally contains an EOCD-looking signature.

Understand the failure class

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/8649e5defd705436. Report an issue: GitHub.