MuntashirAkon/AppManager · error · IOException
Corrupted input, nSelectors value negative
Error message
Corrupted input, nSelectors value negative
What it means
recvDecodingTables reads the 15-bit nSelectors count from the block header. Valid bzip2 data always has a non-negative nSelectors; a negative value means the bitstream is corrupt, so the decompressor aborts with this explicit message instead of allocating absurd memory or mis-decoding.
Source
Thrown at app/src/main/java/org/apache/commons/compress/compressors/bzip2/BZip2CompressorInputStream.java:478
Arrays.fill(inUse, false);
for (int i = 0; i < 16; i++) {
if ((inUse16 & (1 << i)) != 0) {
final int i16 = i << 4;
for (int j = 0; j < 16; j++) {
if (bsGetBit(bin)) {
inUse[i16 + j] = true;
}
}
}
}
makeMaps();
final int alphaSize = this.nInUse + 2;
/* Now the selectors */
final int nGroups = bsR(bin, 3);
final int selectors = bsR(bin, 15);
if (selectors < 0) {
throw new IOException("Corrupted input, nSelectors value negative");
}
checkBounds(alphaSize, MAX_ALPHA_SIZE + 1, "alphaSize");
checkBounds(nGroups, N_GROUPS + 1, "nGroups");
// Don't fail on nSelectors overflowing boundaries but discard the values in overflow
// See https://gnu.wildebeest.org/blog/mjw/2019/08/02/bzip2-and-the-cve-that-wasnt/
// and https://sourceware.org/ml/bzip2-devel/2019-q3/msg00007.html
for (int i = 0; i < selectors; i++) {
int j = 0;
while (bsGetBit(bin)) {
j++;
}
if (i < MAX_SELECTORS) {
selectorMtf[i] = (byte) j;
}
}
final int nSelectors = selectors > MAX_SELECTORS ? MAX_SELECTORS : selectors;View on GitHub (pinned to 0152f468fc)
Solutions
- Validate the source archive (`bzip2 -t`) and get a clean copy.
- Confirm the InputStream position: don't start decompression mid-file or after full consumption.
- Catch IOException around read loops and surface a user-facing 'corrupt archive' error.
- Check storage/network for bit-rot if corruption recurs on many files.
Example fix
// before
while ((n = bz.read(buf)) >= 0) out.write(buf, 0, n); // raw IOException escapes
// after
try {
while ((n = bz.read(buf)) >= 0) out.write(buf, 0, n);
} catch (IOException e) {
throw new CorruptArchiveException("bad bzip2 data: " + e.getMessage(), e);
} Defensive patterns
Strategy: try-catch
Validate before calling
Process p = new ProcessBuilder("bzip2", "-t", path.toString()).redirectErrorStream(true).start();
if (p.waitFor() != 0) throw new IOException("archive failed integrity check: " + path); Try / catch
try (BZip2CompressorInputStream bz = new BZip2CompressorInputStream(in)) {
IOUtils.copy(bz, out);
} catch (IOException e) {
throw new CorruptArchiveException("nSelectors/block-header corruption: " + e.getMessage(), e);
} Prevention
- Run integrity checks on archives at ingestion time
- Do not resume or seek within bzip2 streams mid-block
- Watch for repeated corruption — check storage/network health
- Log the offending file path with the exception
When it happens
Trigger: getAndMoveToFrontDecode -> recvDecodingTables encounters a corrupted block header while BZip2CompressorInputStream is reading a block; the 15-bit selector count decodes negative.
Common situations: Truncated or corrupted .bz2 files, non-bzip2 bytes fed into the stream, transfer corruption, security fuzzing inputs.
Related errors
- Corrupted input, " + name + " value negative
- Corrupted input, " + name + " value too big
- Block overrun while expanding RLE in MTF, " + lastShadow + "
- Block overrun in MTF, " + lastShadow + " exceeds " + limitLa
- Stream corrupted
AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12).
Data as JSON: /api/errors/d9d390048f7f4f8e.
Report an issue: GitHub.