Tencent/tinker · error · ZipException
STORED entry size/compressed size mismatch
Error message
STORED entry size/compressed size mismatch
What it means
For a STORED (uncompressed) entry the compressed size must equal the uncompressed size by definition. After the missing-size checks pass, putNextEntry() compares ze.size != ze.compressedSize and throws ZipException("STORED entry size/compressed size mismatch") when the caller supplied two different values. This catches inconsistent metadata copied or computed by the caller before writing a corrupt local file header.
Source
Thrown at third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipOutputStream.java:467
int method = ze.getMethod();
if (method == -1) {
method = defaultCompressionMethod;
}
// If the method is STORED, check that the ZipEntry was configured appropriately.
if (method == STORED) {
if (ze.getCompressedSize() == -1) {
ze.setCompressedSize(ze.getSize());
} else if (ze.getSize() == -1) {
ze.setSize(ze.getCompressedSize());
}
if (ze.getCrc() == -1) {
throw new ZipException("STORED entry missing CRC");
}
if (ze.getSize() == -1) {
throw new ZipException("STORED entry missing size");
}
if (ze.size != ze.compressedSize) {
throw new ZipException("STORED entry size/compressed size mismatch");
}
}
checkOpen();
// checkAndSetZip64Requirements(ze);
//zhangshaowen edit here, we just want the same time and modDate
ze.comment = null;
ze.extra = null;
ze.time = TIME_CONST;
ze.modDate = MOD_DATE_CONST;
nameBytes = ze.name.getBytes(StandardCharsets.UTF_8);
checkSizeIsWithinShort("Name", nameBytes);
entryCommentBytes = BYTE;
if (ze.comment != null) {
entryCommentBytes = ze.comment.getBytes(StandardCharsets.UTF_8);
// The comment is not written out until the entry is finished, but it is validated here
// to fail-fast.View on GitHub (pinned to 1b7ea02c23)
Solutions
- For STORED entries always set compressedSize = size explicitly: ze.setCompressedSize(ze.getSize()) (or set both to the same buffer length).
- When copying from a source entry, recompute both fields from the raw data length rather than trusting source compressedSize.
- Add an assert before putNextEntry: if method == STORED, assert size == compressedSize.
Example fix
// before e.setSize(rawData.length); e.setCompressedSize(srcEntry.getCompressedSize()); // deflated length: mismatch zos.putNextEntry(e); // after e.setSize(rawData.length); e.setCompressedSize(rawData.length); // STORED: must equal size zos.putNextEntry(e);
Defensive patterns
Strategy: validation
Validate before calling
static void assertStoredEntryConsistent(TinkerZipEntry e) {
if (e.getMethod() == TinkerZipEntry.STORED && e.getSize() != e.getCompressedSize()) {
throw new IllegalStateException("STORED size mismatch for " + e.getName());
}
}
// call before putNextEntry(e) Prevention
- For STORED entries always setCompressedSize(getSize()) as the final metadata step
- When copying from a DEFLATED source, recompute both sizes from raw data length
When it happens
Trigger: putNextEntry(entry) with method STORED where setSize() and setCompressedSize() were both called with different values — typically size set from the original file but compressedSize copied from a DEFLATED source entry (or vice versa), or each populated from different variables in a copy loop.
Common situations: Copying entry metadata from a source zip that was DEFLATED into a STORED output: compressedSize (deflated length) differs from size (raw length); refactoring where one of the two setters is fed a different length variable; stale compressedSize left over after switching an entry's method to STORED.
Related errors
- STORED entry missing CRC
- STORED entry missing size
- CRC mismatch
- Size mismatch
- Size mismatch on inflated file: ${bytesRead} vs ${entry.size
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/890f26c5996bbd43.
Report an issue: GitHub.