MuntashirAkon/AppManager · error · IOException

Failed to read Paxheader. Expected

Error message

Failed to read Paxheader. Expected ${restLen} bytes, read ${got}

What it means

Thrown by TarUtils.parsePaxHeaders when IOUtils.readFully cannot read the declared restLen bytes of a PAX keyword's value from the stream, i.e. the stream hit EOF prematurely. The PAX record promised more data than the stream contains.

Solutions

  1. Check the archive file size against the expected size and re-download if truncated
  2. Validate the source stream isn't closed or failing upstream
  3. Catch IOException and treat the archive as corrupt
  4. Re-create the archive with a standard tar tool
Defensive patterns

Strategy: validation

Validate before calling

long expectedSize = getExpectedArchiveSize();
if (archiveFile.length() != expectedSize) {
    throw new IOException("Archive truncated: " + archiveFile.length() + " of " + expectedSize + " bytes");
}

Try / catch

try {
    // read tar
} catch (IOException e) {
    if (e.getMessage().startsWith("Failed to read Paxheader")) {
        throw new TruncatedArchiveException("Download incomplete", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Reading a TAR whose PAX header value length field is larger than the actual remaining stream bytes; happens on truncated downloads or when the underlying InputStream ends mid PAX record.

Common situations: Incomplete file downloads, archives cut off by interrupted transfers or partial uploads, reading from a closed/socket stream that ended early.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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

Appendix: source

Thrown at app/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java:716

                        totalRead++;
                        if (totalRead < 0 || (headerSize >= 0 && totalRead >= headerSize)) {
                             break;
                         }
                        if (ch == '='){ // end of keyword
                            final String keyword = coll.toString(CharsetNames.UTF_8);
                            // Get rest of entry
                            final int restLen = len - read;
                            if (restLen <= 1) { // only NL
                                headers.remove(keyword);
                            } else if (headerSize >= 0 && restLen > headerSize - totalRead) {
                                throw new IOException("Paxheader value size " + restLen
                                    + " exceeds size of header record");

                            } else {
                                final byte[] rest = new byte[restLen];
                                final int got = IOUtils.readFully(inputStream, rest);
                                if (got != restLen) {
                                    throw new IOException("Failed to read "
                                            + "Paxheader. Expected "
                                            + restLen
                                            + " bytes, read "
                                            + got);
                                }
                                totalRead += restLen;
                                // Drop trailing NL
                                if (rest[restLen - 1] != '\n') {
                                    throw new IOException("Failed to read Paxheader."
                                       + "Value should end with a newline");
                                }
                                final String value = new String(rest, 0,
                                        restLen - 1, StandardCharsets.UTF_8);
                                headers.put(keyword, value);

                                // for 0.0 PAX Headers
                                if (keyword.equals("GNU.sparse.offset")) {
                                    if (offset != null) {

View on GitHub (pinned to 0152f468fc)