MuntashirAkon/AppManager · error · IOException

This archive has already been finished

Error message

This archive has already been finished

What it means

TarArchiveOutputStream.finish() writes the end-of-archive records; calling it more than once is invalid, so the second call throws IOException because the internal 'finished' flag is already set. close() calls finish(), so manual finish() followed by close() triggers this.

Solutions

  1. Call finish() only once, or rely on close() which finishes automatically.
  2. Track whether you already finished and skip the second call.
  3. If using try-with-resources, remove the explicit finish() call.

Example fix

// before
tar.finish();
tar.close(); // finish() runs again -> throws
// after
tar.close(); // close() finishes the archive exactly once
Defensive patterns

Strategy: try-catch

Validate before calling

// Track finish state yourself
boolean archiveFinished = false;
if (!archiveFinished) { tar.finish(); archiveFinished = true; }

Try / catch

try {
    tar.finish();
} catch (IOException e) {
    if (!"This archive has already been finished".equals(e.getMessage())) throw e;
    // already finished: safe to ignore
}

Prevention

When it happens

Trigger: Calling finish() then close(); calling finish() twice; wrapping the stream in code that both finishes and closes (e.g. try-with-resources after an explicit finish()).

Common situations: Manual resource management without try-with-resources; helper methods that finish the archive while a caller also closes it; retry logic that re-invokes finish after a partial failure.

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


AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12). Data as JSON: /api/errors/df27c8fff8e7725f. Report an issue: GitHub.

Appendix: source

Thrown at app/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java:272

    @Override
    public long getBytesWritten() {
        return countingOut.getBytesWritten();
    }

    /**
     * Ends the TAR archive without closing the underlying OutputStream.
     *
     * An archive consists of a series of file entries terminated by an
     * end-of-archive entry, which consists of two 512 blocks of zero bytes.
     * POSIX.1 requires two EOF records, like some other implementations.
     *
     * @throws IOException on error
     */
    @Override
    public void finish() throws IOException {
        if (finished) {
            throw new IOException("This archive has already been finished");
        }

        if (haveUnclosedEntry) {
            throw new IOException("This archive contains unclosed entries.");
        }
        writeEOFRecord();
        writeEOFRecord();
        padAsNeeded();
        out.flush();
        finished = true;
    }

    /**
     * Closes the underlying OutputStream.
     *
     * @throws IOException on error
     */
    @Override

View on GitHub (pinned to 0152f468fc)