MuntashirAkon/AppManager · error · IOException

Corrupted struct sparse detected

Error message

Corrupted struct sparse detected

What it means

Thrown by TarArchiveInputStream.buildSparseInputStreams when a GNU/pax sparse-file header lists a chunk whose offset is less than the current stream offset, meaning the sparse entries are not monotonically increasing. This indicates the archive's sparse metadata is corrupt, so the library refuses to build the sparse input stream list rather than produce a wrongly reassembled file.

Source

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

            final Comparator<TarArchiveStructSparse> sparseHeaderComparator = (p, q) -> {
                final Long pOffset = p.getOffset();
                final Long qOffset = q.getOffset();
                return pOffset.compareTo(qOffset);
            };
            Collections.sort(sparseHeaders, sparseHeaderComparator);
        }

        if (sparseHeaders != null) {
            // Stream doesn't need to be closed at all as it doesn't use any resources
            final InputStream zeroInputStream = new TarArchiveSparseZeroInputStream(); //NOSONAR
            long offset = 0;
            for (final TarArchiveStructSparse sparseHeader : sparseHeaders) {
                if (sparseHeader.getOffset() == 0 && sparseHeader.getNumbytes() == 0) {
                    break;
                }

                if ((sparseHeader.getOffset() - offset) < 0) {
                    throw new IOException("Corrupted struct sparse detected");
                }

                // only store the input streams with non-zero size
                if ((sparseHeader.getOffset() - offset) > 0) {
                    sparseInputStreams.add(new BoundedInputStream(zeroInputStream, sparseHeader.getOffset() - offset));
                }

                // only store the input streams with non-zero size
                if (sparseHeader.getNumbytes() > 0) {
                    sparseInputStreams.add(new BoundedInputStream(inputStream, sparseHeader.getNumbytes()));
                }

                offset = sparseHeader.getOffset() + sparseHeader.getNumbytes();
            }
        }

        if (!sparseInputStreams.isEmpty()) {
            currentSparseInputStreamIndex = 0;

View on GitHub (pinned to 0152f468fc)

Solutions

  1. Verify the archive integrity (checksum, re-download) — the file itself is likely corrupt.
  2. Re-create the archive with a standard tool (GNU tar with -S for sparse files) instead of hand-building sparse headers.
  3. If you control archive generation, ensure sparse offsets are strictly increasing and cover the file contiguously.
  4. If you don't need sparse support, write the archive as a normal (non-sparse) tar entry.

Example fix

// before: trusting a third-party sparse tar
ew TarArchiveInputStream(new FileInputStream(f)) // throws on corrupt sparse headers
// after: validate source first
if (!checksumMatches(f, expectedSha256)) throw new IOException("archive corrupt, re-fetch");
Defensive patterns

Strategy: validation

Validate before calling

// Before reading a sparse entry, sanity-check each sparse header
for (TarArchiveStructSparse s : sparseHeaders) {
    if (s.getOffset() < lastOffset) throw new IOException("non-monotonic sparse offset: " + s.getOffset());
    lastOffset = s.getOffset() + s.getNumbytes();
}

Try / catch

try (TarArchiveInputStream tis = new TarArchiveInputStream(in)) {
    // read entries
} catch (IOException e) {
    if (e.getMessage().contains("Corrupted struct sparse")) {
        // quarantine/re-fetch archive
    } else throw e;
}

Prevention

When it happens

Trigger: Reading a tar entry whose pax or old-GNU sparse headers (via paxHeaders or readOldGNUSparse) contain a sparse struct whose offset is smaller than the previous chunk's offset+size; typically a truncated, hand-edited, or maliciously crafted archive.

Common situations: Processing untrusted or partially downloaded tar archives; archives transferred in text mode corrupting header bytes; hand-crafted sparse entries; fuzzed/security-scanned inputs (this check exists as a hardening against crafted archives).

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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