MuntashirAkon/AppManager · error · IOException
Stream has already been finished
Error message
Stream has already been finished
What it means
putArchiveEntry checks the finished flag and throws IOException if the archive was already finished, since no new entries can be added after the end-of-archive records were written. finish() may have been called directly or via close().
Solutions
- Create a new TarArchiveOutputStream if you need to keep writing.
- Reorder logic so all putArchiveEntry/closeArchiveEntry pairs happen before finish/close.
- Guard entry-writing code paths with a check on your own 'archive open' state.
Example fix
// before
tar.close();
tar.putArchiveEntry(entry); // throws
// after
try (TarArchiveOutputStream tar2 = new TarArchiveOutputStream(out2)) {
tar2.putArchiveEntry(entry);
tar2.closeArchiveEntry();
} Defensive patterns
Strategy: try-catch
Validate before calling
// Guard with your own lifecycle flag
if (archiveClosed) throw new IllegalStateException("archive already closed"); Try / catch
try {
tar.putArchiveEntry(entry);
} catch (IOException e) {
if ("Stream has already been finished".equals(e.getMessage())) {
// reopen a fresh stream or abort the batch
} else throw e;
} Prevention
- Keep one owner of the stream lifecycle
- Never reuse a closed TarArchiveOutputStream
- Create a new stream for a new archive
When it happens
Trigger: Calling putArchiveEntry after finish() or close(); reusing a finished TarArchiveOutputStream for another archive; a helper (handleLongName, writePaxHeaders, addManifestEntry) invoked on a finished stream.
Common situations: Writing archives in a loop where a later iteration uses a closed stream; returning the stream from a method after closing; accidentally closing in a finally block then continuing to write entries.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- No current entry to close
- No current tar entry
- This archive contains unclosed entries.
- This archive has already been finished
- APK files backup is requested but no APK files have been…
AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12).
Data as JSON: /api/errors/40d136ddd910c446.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java:335
* output stream for writing the contents of the entry. Once this method is called, the stream
* is ready for calls to write() to write the entry's contents. Once the contents are written,
* closeArchiveEntry() <B>MUST</B> be called to ensure that all buffered data is completely
* written to the output stream.
*
* @param archiveEntry The TarEntry to be written to the archive.
* @throws IOException on error
* @throws ClassCastException if archiveEntry is not an instance of TarArchiveEntry
* @throws IllegalArgumentException if the {@link TarArchiveOutputStream#longFileMode} equals
* {@link TarArchiveOutputStream#LONGFILE_ERROR} and the file
* name is too long
* @throws IllegalArgumentException if the {@link TarArchiveOutputStream#bigNumberMode} equals
* {@link TarArchiveOutputStream#BIGNUMBER_ERROR} and one of the numeric values
* exceeds the limits of a traditional tar header.
*/
@Override
public void putArchiveEntry(final ArchiveEntry archiveEntry) throws IOException {
if (finished) {
throw new IOException("Stream has already been finished");
}
final TarArchiveEntry entry = (TarArchiveEntry) archiveEntry;
if (entry.isGlobalPaxHeader()) {
final byte[] data = encodeExtendedPaxHeadersContents(entry.getExtraPaxHeaders());
entry.setSize(data.length);
entry.writeEntryHeader(recordBuf, zipEncoding, bigNumberMode == BIGNUMBER_STAR);
writeRecord(recordBuf);
currSize= entry.getSize();
currBytes = 0;
this.haveUnclosedEntry = true;
write(data);
closeArchiveEntry();
} else {
final Map<String, String> paxHeaders = new HashMap<>();
final String entryName = entry.getName();
final boolean paxHeaderContainsPath = handleLongName(entry, entryName, paxHeaders, "path",
TarConstants.LF_GNUTYPE_LONGNAME, "file name");
View on GitHub (pinned to 0152f468fc)