{"record":{"id":"ad0ff3abacbbc20c","repo":"prestodb/presto","slug":"druid-segment-load-error-ad0ff3","errorCode":"DRUID_SEGMENT_LOAD_ERROR","errorMessage":"Malformed End of Central Directory Record; does not start with %08x","messagePattern":"Malformed End of Central Directory Record; does not start with %08x","errorType":"error_code","errorClass":"PrestoException","httpStatus":null,"severity":"critical","filePath":"presto-druid/src/main/java/com/facebook/presto/druid/zip/EndOfCentralDirectoryRecord.java","lineNumber":53,"sourceCode":"\n    private EndOfCentralDirectoryRecord()\n    {\n    }\n\n    /**\n     * Read the end of central directory record from the input stream and parse {@link ZipFileData}\n     * from it.\n     */\n    public static void read(ZipFileData zipFileData, DataInputSource dataInputSource, long offset)\n            throws IOException\n    {\n        long position = offset;\n        byte[] fixedSizeData = new byte[FIXED_DATA_SIZE];\n\n        dataInputSource.readFully(position, fixedSizeData, 0, FIXED_DATA_SIZE);\n        position += FIXED_DATA_SIZE;\n        if (!ZipUtil.arrayStartsWith(fixedSizeData, ZipUtil.intToLittleEndian(SIGNATURE))) {\n            throw new PrestoException(DRUID_SEGMENT_LOAD_ERROR, format(\"Malformed End of Central Directory Record; does not start with %08x\", SIGNATURE));\n        }\n\n        byte[] comment = new byte[ZipUtil.getUnsignedShort(fixedSizeData, COMMENT_LENGTH_OFFSET)];\n        if (comment.length > 0) {\n            dataInputSource.readFully(position, comment, 0, comment.length);\n        }\n        short diskNumber = ZipUtil.get16(fixedSizeData, DISK_NUMBER_OFFSET);\n        short centralDirectoryDisk = ZipUtil.get16(fixedSizeData, CD_DISK_OFFSET);\n        short entriesOnDisk = ZipUtil.get16(fixedSizeData, DISK_ENTRIES_OFFSET);\n        short totalEntries = ZipUtil.get16(fixedSizeData, TOTAL_ENTRIES_OFFSET);\n        int centralDirectorySize = ZipUtil.get32(fixedSizeData, CD_SIZE_OFFSET);\n        int centralDirectoryOffset = ZipUtil.get32(fixedSizeData, CD_OFFSET_OFFSET);\n        if (diskNumber == -1 || centralDirectoryDisk == -1 || entriesOnDisk == -1\n                || totalEntries == -1 || centralDirectorySize == -1 || centralDirectoryOffset == -1) {\n            zipFileData.setMaybeZip64(true);\n        }\n        zipFileData.setComment(comment);\n        zipFileData.setCentralDirectorySize(ZipUtil.getUnsignedInt(fixedSizeData, CD_SIZE_OFFSET));","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-druid/src/main/java/com/facebook/presto/druid/zip/EndOfCentralDirectoryRecord.java#L35-L71","documentation":"EndOfCentralDirectoryRecord.read reads the fixed 22-byte EOCD structure and requires it to start with signature 0x06054b50. If the bytes located by findEndOfCentralDirectoryRecord do not begin with that signature, the record is malformed and DRUID_SEGMENT_LOAD_ERROR is thrown.","triggerScenarios":"read(offset) called with an offset whose 22 bytes lack the EOCD signature — typically when a backwards scan matched a false signature inside a ZIP comment or file data, or the region was corrupted.","commonSituations":"Zips with file comments or embedded data containing the EOCD signature bytes causing mislocation, corruption in the final 64KB of the file, or partial downloads that cut the record in half while keeping an earlier false-positive signature.","solutions":["Re-upload the segment zip after verifying it with `unzip -t`.","Rebuild the archive with a standard tool if it contains nonstandard comments/data known to trip scanners.","Compare checksums of the stored object against the producer's to pinpoint when corruption occurred.","Retry reading from a different replica if deep storage provides multiple."],"exampleFix":"// before\nvar eocd = EndOfCentralDirectoryRecord.read(dataInputSource, offset);\n// after: verify tail bytes before parsing\nlong size = dataInputSource.getSize();\ncheckState(size >= offset + 22, \"EOCD offset %s out of range for %s\", offset, dataInputSource.getId());\nvar eocd = EndOfCentralDirectoryRecord.read(dataInputSource, offset);","handlingStrategy":"validation","validationCode":"if (offset + 22 > dataInputSource.getSize()) {\n    throw new IllegalStateException(\"EOCD offset out of range: \" + offset);\n}","typeGuard":null,"tryCatchPattern":"try {\n    eocd = EndOfCentralDirectoryRecord.read(source, offset);\n} catch (PrestoException e) {\n    log.error(\"Malformed EOCD in %s at offset %s\", source.getId(), offset, e);\n    retryWithReplicaOrReIngest(segmentId);\n}","preventionTips":["Avoid archives with huge or binary file comments that can confuse EOCD scanning.","Validate archives at upload time with unzip -t or a pure-Java probe.","Keep multiple replicas and fall back on read failure."],"tags":["zip","druid","eocd","corrupt-file"],"backgroundTag":"malformed-zip-missing-eocd","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}