MuntashirAkon/AppManager · error · IOException

Failed to read Paxheader.GNU.sparse.offset is expected…

Error message

Failed to read Paxheader.GNU.sparse.offset is expected before GNU.sparse.numbytes shows up.

What it means

Thrown by TarUtils.parsePaxHeaders when a GNU.sparse.numbytes record is encountered without a preceding GNU.sparse.offset record. GNU sparse file format 0.0 encodes extents as offset/numbytes pairs, and order matters; a numbytes without an offset indicates malformed PAX data.

Solutions

  1. Re-pack the sparse archive with GNU tar (tar -S --format=pax) so offset/numbytes pairs are emitted in order
  2. Ensure your writer always emits GNU.sparse.offset immediately before each GNU.sparse.numbytes
  3. If sparse support isn't needed, extract the archive with a tool that expands sparse files first
  4. Catch IOException and treat the archive as malformed
Defensive patterns

Strategy: try-catch

Try / catch

try {
    // read tar
} catch (IOException e) {
    if (e.getMessage().contains("GNU.sparse.offset is expected")) {
        throw new CorruptArchiveException("GNU sparse PAX records out of order", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Reading a TAR with GNU sparse 0.0 PAX headers where the numbytes keyword appears before any offset keyword, or after the pair was already consumed and offset reset to null.

Common situations: Archives created by buggy sparse-file writers, or PAX headers edited/reordered by other tools that dropped the offset record.

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/54c89fb7b24eabe1. Report an issue: GitHub.

Appendix: source

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

                                       + "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) {
                                        // previous GNU.sparse.offset header but but no numBytes
                                        sparseHeaders.add(new TarArchiveStructSparse(offset, 0));
                                    }
                                    offset = Long.valueOf(value);
                                }

                                // for 0.0 PAX Headers
                                if (keyword.equals("GNU.sparse.numbytes")) {
                                    if (offset == null) {
                                        throw new IOException("Failed to read Paxheader." +
                                                "GNU.sparse.offset is expected before GNU.sparse.numbytes shows up.");
                                    }
                                    sparseHeaders.add(new TarArchiveStructSparse(offset, Long.parseLong(value)));
                                    offset = null;
                                }
                            }
                            break;
                        }
                        coll.write((byte) ch);
                    }
                    break; // Processed single header
                }

                // COMPRESS-530 : throw if we encounter a non-number while reading length
                if (ch < '0' || ch > '9') {
                    throw new IOException("Failed to read Paxheader. Encountered a non-number while reading length");
                }

View on GitHub (pinned to 0152f468fc)