{"record":{"id":"8bd3d2ead41d2a40","repo":"Tencent/tinker","slug":"file-too-short-to-be-a-zip-file-raf-length","errorCode":null,"errorMessage":"File too short to be a zip file: ${raf.length()}","messagePattern":"File too short to be a zip file: (.+?)","errorType":"exception","errorClass":"ZipException","httpStatus":null,"severity":"error","filePath":"third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipFile.java","lineNumber":422,"sourceCode":"     *\n     * <p>The central directory can be followed by a variable-length comment\n     * field, so we have to scan through it backwards.  The comment is at\n     * most 64K, plus we have 18 bytes for the end-of-central-dir stuff\n     * itself, plus apparently sometimes people throw random junk on the end\n     * just for the fun of it.\n     *\n     * <p>This is all a little wobbly.  If the wrong value ends up in the EOCD\n     * area, we're hosed. This appears to be the way that everybody handles\n     * it though, so we're in good company if this fails.\n     */\n    private void readCentralDir() throws IOException {\n        // Scan back, looking for the End Of Central Directory field. If the zip file doesn't\n        // have an overall comment (unrelated to any per-entry comments), we'll hit the EOCD\n        // on the first try.\n        // No need to synchronize raf here -- we only do this when we first open the zip file.\n        long scanOffset = raf.length() - ENDHDR;\n        if (scanOffset < 0) {\n            throw new ZipException(\"File too short to be a zip file: \" + raf.length());\n        }\n\n        raf.seek(0);\n        final int headerMagic = Integer.reverseBytes(raf.readInt());\n        if (headerMagic != LOCSIG) {\n            throw new ZipException(\"Not a zip archive\");\n        }\n\n        long stopOffset = scanOffset - 65536;\n        if (stopOffset < 0) {\n            stopOffset = 0;\n        }\n\n        while (true) {\n            raf.seek(scanOffset);\n            if (Integer.reverseBytes(raf.readInt()) == ENDSIG) {\n                break;\n            }","sourceCodeStart":404,"sourceCodeEnd":440,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipFile.java#L404-L440","documentation":"In readCentralDir, TinkerZipFile first computes raf.length() - ENDHDR (ENDHDR is the 22-byte EOCD record size); if the file is shorter than that, there is no room for even a minimal End Of Central Directory record, so it throws ZipException('File too short to be a zip file: <length>'). The length is included so you can immediately distinguish a truncated download from other corruption.","triggerScenarios":"Constructing TinkerZipFile on a file smaller than 22 bytes: empty files (0 bytes), 1-byte stubs, HTML error pages saved with a .zip extension, or a download that created the file but wrote nothing.","commonSituations":"HTTP error responses saved as the payload; a downloader creating the target file then failing before writing; passing a directory or wrong path that resolves to a tiny placeholder; test fixtures that are empty by accident.","solutions":["Check the reported length: 0 or a few hundred bytes almost always means a failed download — re-fetch the file.","Validate the downloaded artifact's size/ checksum against the server-provided Content-Length before opening.","Guard with a minimum-size check (>= 22 bytes plus entry data) in your pipeline and reject earlier with a clearer message."],"exampleFix":"// before\nTinkerZipFile zf = new TinkerZipFile(new File(path)); // may be a 0-byte stub\n\n// after\nFile f = new File(path);\nif (f.length() < 22) {\n    throw new IOException(\"Incomplete download: \" + path + \" is only \" + f.length() + \" bytes\");\n}\nTinkerZipFile zf = new TinkerZipFile(f);","handlingStrategy":"validation","validationCode":"static final long MIN_ZIP_BYTES = 22; // ENDHDR\n\nif (file.length() < MIN_ZIP_BYTES) {\n    throw new IOException(\"File too small to be a zip (\" + file.length() + \" bytes): \" + file);\n}\nTinkerZipFile zf = new TinkerZipFile(file);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Verify downloaded file size (Content-Length/checksum) before opening.","Reject 0-byte files at ingestion — they usually mean a failed transfer.","Keep a minimum-size guard in every pipeline stage that receives files from the network."],"tags":["zip","truncation","download","tinker"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}