MuntashirAkon/AppManager · error · IOException

Truncated TAR archive

Error message

Truncated TAR archive

What it means

Thrown by getActuallySkipped when the underlying stream could not skip as many bytes as requested while reading an entry or its padding. Because skipping fewer bytes than expected desynchronizes the parser, the stream reports the archive as truncated.

Solutions

  1. Verify the archive's size against the source (re-download; compare byte counts/hashes)
  2. Use streams that report correct available/skip semantics (avoid wrapping in streams that short-circuit skip)
  3. Wait until writing of the tar is complete before reading
  4. Catch IOException, and consider implementing a manual read-loop skip for streams with unreliable skip

Example fix

// before
long n = in.skip(entrySize); // may skip fewer bytes silently elsewhere
// after
long remaining = entrySize;
byte[] buf = new byte[8192];
while (remaining > 0) {
    long r = in.read(buf, 0, (int) Math.min(buf.length, remaining));
    if (r < 0) throw new IOException("Truncated TAR archive");
    remaining -= r;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (archiveFile.length() < expectedMinimumSize) {
    throw new IOException("archive incomplete");
}

Try / catch

try {
    // iterate entries, read data
} catch (IOException e) {
    if (e.getMessage().contains("Truncated TAR archive")) {
        throw new IOException("Archive incomplete — re-download required", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: skip(long) or skipRecordPadding() reaches EOF before the full entry data/padding has been consumed — the archive file is shorter than its headers claim.

Common situations: Partially downloaded or interrupted transfers; archives copied with truncation (disk-full during copy); reading a still-being-written tar.

Related errors


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

Appendix: source

Thrown at app/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java:465

    /**
     * For FileInputStream, the skip always return the number you input, so we
     * need the available bytes to determine how many bytes are actually skipped
     *
     * @param available available bytes returned by inputStream.available()
     * @param skipped   skipped bytes returned by inputStream.skip()
     * @param expected  bytes expected to skip
     * @return number of bytes actually skipped
     * @throws IOException if a truncated tar archive is detected
     */
    private long getActuallySkipped(final long available, final long skipped, final long expected) throws IOException {
        long actuallySkipped = skipped;
        if (inputStream instanceof FileInputStream) {
            actuallySkipped = Math.min(skipped, available);
        }

        if (actuallySkipped != expected) {
            throw new IOException("Truncated TAR archive");
        }

        return actuallySkipped;
    }

    /**
     * Get the next entry in this tar archive as longname data.
     *
     * @return The next entry in the archive as longname data, or null.
     * @throws IOException on error
     */
    protected byte[] getLongNameData() throws IOException {
        // read in the name
        final ByteArrayOutputStream longName = new ByteArrayOutputStream();
        int length = 0;
        while ((length = read(smallBuf)) >= 0) {
            longName.write(smallBuf, 0, length);
        }

View on GitHub (pinned to 0152f468fc)