Tencent/tinker · error · ZipException

STORED entry missing size

Error message

STORED entry missing size

What it means

For STORED entries the uncompressed and compressed sizes must be known before data is written. putNextEntry() first tries to derive one from the other (if compressedSize == -1 it copies size, and vice versa); if BOTH size and compressedSize are still -1 it throws ZipException("STORED entry missing size"). Unlike DEFLATED, STORED data is written verbatim so sizes cannot be discovered while streaming.

Source

Thrown at third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipOutputStream.java:464

            closeEntry();
        }
        // Did this ZipEntry specify a method, or should we use the default?
        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) {

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Buffer the entry data (byte[] or temp file) so its length is known, then call ze.setSize(len) and ze.setCompressedSize(len) before putNextEntry().
  2. If the data is copied from another zip entry, copy the source entry's size and compressedSize fields across along with crc.
  3. Switch the entry to DEFLATED if the length genuinely cannot be known up front.

Example fix

// before
TinkerZipEntry e = new TinkerZipEntry("assets/blob.bin");
e.setMethod(TinkerZipEntry.STORED);
e.setCrc(crc32);
zos.putNextEntry(e); // throws: missing size

// after
TinkerZipEntry e = new TinkerZipEntry("assets/blob.bin");
e.setMethod(TinkerZipEntry.STORED);
e.setCrc(crc32);
e.setSize(dataLen);
e.setCompressedSize(dataLen);
zos.putNextEntry(e);
Defensive patterns

Strategy: validation

Validate before calling

void putStored(TinkerZipOutputStream zos, String name, byte[] data) throws IOException {
    CRC32 crc = new CRC32();
    crc.update(data);
    TinkerZipEntry e = new TinkerZipEntry(name);
    e.setMethod(TinkerZipEntry.STORED);
    e.setCrc(crc.getValue());
    e.setSize(data.length);
    e.setCompressedSize(data.length);
    zos.putNextEntry(e);
    zos.write(data, 0, data.length);
    zos.closeEntry();
}

Prevention

When it happens

Trigger: putNextEntry(entry) with method STORED where neither entry.setSize(...) nor entry.setCompressedSize(...) was ever called — both remain at their -1 default after the derivation block runs.

Common situations: Streaming a dynamically generated file (unknown length) into a STORED entry; converting a DEFLATED pipeline to STORED and only setting the CRC but not sizes; copying entries from a source zip whose size fields were not read/populated.

Related errors


AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14). Data as JSON: /api/errors/f803db11d6b5dddc. Report an issue: GitHub.