Tencent/tinker · error · ZipException
No active entry
Error message
No active entry
What it means
TinkerZipOutputStream.write(byte[], int, int) throws ZipException("No active entry") when currentEntry == null — i.e. there is no entry between putNextEntry() and closeEntry(). The zip format requires all payload bytes to belong to a local file header, so writing outside an entry would produce a corrupt archive and is rejected eagerly.
Source
Thrown at third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/TinkerZipOutputStream.java:576
this.commentBytes = BYTE;
return;
}
byte[] newCommentBytes = comment.getBytes(StandardCharsets.UTF_8);
checkSizeIsWithinShort("Comment", newCommentBytes);
this.commentBytes = newCommentBytes;
}
/**
* Writes data for the current entry to the underlying stream.
*
* @throws IOException
* If an error occurs writing to the stream
*/
@Override
public void write(byte[] buffer, int offset, int byteCount) throws IOException {
Arrays.checkOffsetAndCount(buffer.length, offset, byteCount);
if (currentEntry == null) {
throw new ZipException("No active entry");
}
/*final long totalBytes = crc.tbytes + byteCount;
if ((totalBytes > Zip64.MAX_ZIP_ENTRY_AND_ARCHIVE_SIZE) && !currentEntryNeedsZip64) {
throw new IOException("Zip entry size (" + totalBytes +
" bytes) cannot be represented in the zip format (needs Zip64)." +
" Set the entry length using ZipEntry#setLength to use Zip64 where necessary.");
}*/
if (currentEntry.getMethod() == STORED) {
out.write(buffer, offset, byteCount);
} else {
out.write(buffer, offset, byteCount);
}
// crc.update(buffer, offset, byteCount);
}
private void checkOpen() throws IOException {
if (cDir == null) {
throw new IOException("Stream is closed");
}View on GitHub (pinned to 1b7ea02c23)
Solutions
- Check the call ordering: every write() must sit between a successful putNextEntry(e) and the matching closeEntry().
- Guard copy helpers with a boolean 'entryOpen' flag (set true on putNextEntry, false on closeEntry) and skip/log writes when it is false.
- If this appears after finish(), the stream is done — create a new TinkerZipOutputStream instead of reusing it.
Example fix
// before
zos.write(buffer, 0, n); // before any putNextEntry
TinkerZipEntry e = new TinkerZipEntry("a.txt");
zos.putNextEntry(e);
// after
TinkerZipEntry e = new TinkerZipEntry("a.txt");
zos.putNextEntry(e);
zos.write(buffer, 0, n);
zos.closeEntry(); Defensive patterns
Strategy: validation
Validate before calling
// maintain explicit entry-open state in copy loops
class ZipCopier {
private boolean entryOpen = false;
void open(TinkerZipOutputStream zos, TinkerZipEntry e) throws IOException {
zos.putNextEntry(e); entryOpen = true;
}
void write(TinkerZipOutputStream zos, byte[] b, int n) throws IOException {
if (!entryOpen) throw new IllegalStateException("write outside entry");
zos.write(b, 0, n);
}
void close(TinkerZipOutputStream zos) throws IOException {
if (entryOpen) { zos.closeEntry(); entryOpen = false; }
}
} Prevention
- Always bracket writes with putNextEntry/closeEntry in a single try block
- Review loop bodies for 'continue'/'break' paths that skip putNextEntry but still write
When it happens
Trigger: Calling write() before the first putNextEntry(); calling write() after closeEntry() for the previous entry but before putNextEntry() for the next; calling write() after finish() has been reached (finish closes the current entry, clearing currentEntry).
Common situations: Copy loops where the source stream is exhausted or the loop body reordered so write happens outside the entry bracket; error paths that call closeEntry() then continue flushing remaining buffers; writing after close().
Related errors
- No entries
- STORED entry missing CRC
- STORED entry missing size
- STORED entry size/compressed size mismatch
- ${property} too long in UTF-8:${bytes.length} bytes
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/f12c5da39f39d40f.
Report an issue: GitHub.