Tencent/tinker · error · IOException
Stream is closed
Error message
Stream is closed
What it means
checkOpen() guards every mutating operation of AlignedZipOutputStream after close() has run; once the closed flag is set, any further putNextEntry, write, or closeEntry throws IOException("Stream is closed"). This is standard use-after-close protection for stream resources, preventing corruption of the already-finalized central directory.
Source
Thrown at third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/AlignedZipOutputStream.java:481
if (currentEntry.getMethod() == STORED) {
out.write(buffer, offset, byteCount);
} else {
super.write(buffer, offset, byteCount);
}
crc.update(buffer, offset, byteCount);
crcDataSize += byteCount;
}
private void checkOffsetAndCount(int arrayLength, int offset, int count) {
if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) {
throw new ArrayIndexOutOfBoundsException("length=" + arrayLength + "; regionStart=" + offset
+ "; regionLength=" + count);
}
}
private void checkOpen() throws IOException {
if (closed) {
throw new IOException("Stream is closed");
}
}
}
View on GitHub (pinned to 1b7ea02c23)
Solutions
- Establish single ownership of the stream: only the owner closes it, and helpers must not close resources they did not create.
- Track stream state in the caller (or wrap with a guard object exposing isOpen()) and check before writing after any operation that may close.
- For deferred writers, keep pending data buffered and write before close in a single serialized phase.
Example fix
// before: helper closes the stream, caller keeps writing
void copyAndClose(InputStream in, OutputStream out) throws IOException {
...; out.close();
}
copyAndClose(in, zos);
zos.putNextEntry(new ZipEntry("later")); // IOException: Stream is closed
// after: helper does not close a stream it does not own
void copy(InputStream in, OutputStream out) throws IOException { ... }
copy(in, zos);
zos.putNextEntry(new ZipEntry("later"));
zos.close(); // owner closes exactly once, last Defensive patterns
Strategy: validation
Validate before calling
// Track lifecycle in the caller and check before writing
if (zipClosed) {
zos = new AlignedZipOutputStream(new java.io.FileOutputStream(outFile, false));
zipClosed = false;
}
zos.putNextEntry(entry); Try / catch
try {
zos.write(buffer, 0, n);
} catch (java.io.IOException e) {
if ("Stream is closed".equals(e.getMessage())) {
// reopen the archive from scratch; partial archives are not resumable
zos = new AlignedZipOutputStream(new java.io.FileOutputStream(outFile, false));
restartWrite();
} else {
throw e;
}
} Prevention
- Give the stream a single owner responsible for close().
- Never let helper methods close streams they did not open.
- Serialize all entry writes before close — no deferred/async writers holding the stream.
When it happens
Trigger: Calling any write-side method after close() — common when a finally block closes the stream but a queued/lazy writer, callback, or retry path still holds a reference and writes later.
Common situations: Utility methods that close the stream they were handed (e.g. a copy() helper closing the output) followed by the caller writing a footer; parallel or deferred tasks (async flushers) outliving the stream; shared streams closed by one code path while another continues appending entries.
Related errors
- Expected ${DEX_IN_JAR_NAME} in ${file}
- CRC mismatch
- Size mismatch
- Entry already exists: {}
- Too many entries for the zip file format's 16-bit entry coun
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/6cc32e46ab4603fc.
Report an issue: GitHub.