{"record":{"id":"f53ca4e465ffd085","repo":"Tencent/tinker","slug":"crc-mismatch","errorCode":null,"errorMessage":"CRC mismatch","messagePattern":"CRC mismatch","errorType":"exception","errorClass":"ZipException","httpStatus":null,"severity":"error","filePath":"third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/AlignedZipOutputStream.java","lineNumber":155,"sourceCode":"     * Closes the current {@code ZipEntry}. Any entry terminal data is written\r\n     * to the underlying stream.\r\n     *\r\n     * @throws IOException\r\n     *             If an error occurs closing the entry.\r\n     */\r\n    public void closeEntry() throws IOException {\r\n        checkOpen();\r\n        if (currentEntry == null) {\r\n            return;\r\n        }\r\n        if (currentEntry.getMethod() == DEFLATED) {\r\n            super.finish();\r\n        }\r\n\r\n        // Verify values for STORED types\r\n        if (currentEntry.getMethod() == STORED) {\r\n            if (crc.getValue() != currentEntry.getCrc()) {\r\n                throw new ZipException(\"CRC mismatch\");\r\n            }\r\n            if (currentEntry.getSize() != crcDataSize) {\r\n                throw new ZipException(\"Size mismatch\");\r\n            }\r\n        }\r\n\r\n        int curOffset = LOCHDR;\r\n\r\n        // Write the DataDescriptor\r\n        if (currentEntry.getMethod() != STORED) {\r\n            curOffset += EXTHDR;\r\n            writeLong(out, EXTSIG);\r\n            currentEntry.setCrc(crc.getValue());\r\n            writeLong(out, currentEntry.getCrc());\r\n            currentEntry.setCompressedSize(def.getTotalOut());\r\n            writeLong(out, currentEntry.getCompressedSize());\r\n            currentEntry.setSize(def.getTotalIn());\r\n            writeLong(out, currentEntry.getSize());\r","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/AlignedZipOutputStream.java#L137-L173","documentation":"For STORED (uncompressed) zip entries, AlignedZipOutputStream requires the caller to pre-compute and declare both CRC and size on the ZipEntry before writing, because STORED data is copied verbatim and must be self-describing. closeEntry() compares the CRC of the bytes actually written (maintained in this.crc) against currentEntry.getCrc(); any difference throws 'CRC mismatch'. This mirrors java.util.zip.ZipOutputStream's behavior for STORED entries.","triggerScenarios":"putNextEntry(new ZipEntry(name)) with method STORED and a setCrc() value that does not match the bytes subsequently written via write(); or forgetting to set the CRC on an entry copied from another zip after modifying its content.","commonSituations":"Copying entries between zips (tinker patch building, re-aligning APKs) and keeping the source entry's CRC while writing different bytes; computing CRC over the wrong byte range (e.g. including/excluding a padding byte); using STORED with data whose size/CRC are only known after compression elsewhere.","solutions":["Before writing a STORED entry, compute the CRC over the exact bytes you will write: byte[] data = ...; crc.reset(); crc.update(data); entry.setCrc(crc.getValue()); entry.setSize(data.length); entry.setCompressedSize(data.length).","When cloning entries from a source zip, stream the entry bytes and recompute size/CRC yourself instead of trusting the source entry's metadata if the content may differ.","If sizes/CRCs are not known up front, use DEFLATED (the stream writes a data descriptor and computes values for you) or buffer the content first."],"exampleFix":"// before: STORED entry with stale/absent CRC\nzos.putNextEntry(new ZipEntry(\"assets/blob.bin\")); // method defaults may be STORED\nzos.write(data); // closeEntry() throws CRC mismatch\n\n// after: declare exact size and CRC for STORED data\nCRC32 crc = new CRC32();\ncrc.update(data);\nZipEntry e = new ZipEntry(\"assets/blob.bin\");\ne.setMethod(ZipEntry.STORED);\ne.setSize(data.length);\ne.setCompressedSize(data.length);\ne.setCrc(crc.getValue());\nzos.putNextEntry(e);\nzos.write(data);","handlingStrategy":"validation","validationCode":"// Precompute size+CRC for STORED entries before putNextEntry\njava.util.zip.CRC32 crc = new java.util.zip.CRC32();\ncrc.update(data);\nif (entry.getMethod() == java.util.zip.ZipEntry.STORED\n        && (entry.getCrc() != crc.getValue() || entry.getSize() != data.length)) {\n    entry.setSize(data.length);\n    entry.setCompressedSize(data.length);\n    entry.setCrc(crc.getValue());\n}","typeGuard":null,"tryCatchPattern":"try {\n    zos.closeEntry();\n} catch (java.util.zip.ZipException e) {\n    if (\"CRC mismatch\".equals(e.getMessage())) {\n        throw new IllegalStateException(\"STORED entry \" + entry.getName() + \" declared a CRC that does not match written bytes\", e);\n    }\n    throw e;\n}","preventionTips":["Always buffer STORED content and set size, compressedSize, and CRC from the exact payload before putNextEntry.","Prefer DEFLATED when content metadata is not known up front.","When cloning entries from another zip, recompute CRC/size instead of copying the source entry's values."],"tags":["zip","stored-entry","crc","tinker"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}