skylot/jadx · error · IllegalStateException
Read limit exceeded
Error message
Read limit exceeded
What it means
A deliberate security guard. LimitedInputStream counts every byte read and throws IllegalStateException once currentPos exceeds maxSize, where maxSize is the entry's declared uncompressed size. It is an anti-zip-bomb measure: if the real decompressed content is larger than the header claims, reading stops immediately. Because it is an unchecked exception it propagates out of read()/read(byte[]) and up through getBytes()/getInputStream() consumers.
Source
Thrown at jadx-commons/jadx-zip/src/main/java/jadx/zip/io/LimitedInputStream.java:21
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
public class LimitedInputStream extends FilterInputStream {
private final long maxSize;
private long currentPos;
private long markPos;
public LimitedInputStream(InputStream in, long maxSize) {
super(in);
this.maxSize = maxSize;
}
private void addAndCheckPos(long count) {
currentPos += count;
if (currentPos > maxSize) {
throw new IllegalStateException("Read limit exceeded");
}
}
@Override
public int read() throws IOException {
int data = super.read();
if (data != -1) {
addAndCheckPos(1);
}
return data;
}
@SuppressWarnings("NullableProblems")
@Override
public int read(byte[] b, int off, int len) throws IOException {
int count = super.read(b, off, len);
if (count > 0) {
addAndCheckPos(count);View on GitHub (pinned to e738a26571)
Solutions
- Treat this as evidence of a tampered or hostile archive; do not trust the entry's data.
- Verify the archive against a known-good checksum and re-download if it mismatches.
- If you control the source and the size header is simply wrong, repackage the zip with a correct uncompressed size.
- Catch IllegalStateException at the call site and skip the entry rather than aborting the whole archive.
Example fix
// before
byte[] bytes = zipEntry.getBytes();
// after
byte[] bytes;
try {
bytes = zipEntry.getBytes();
} catch (IllegalStateException limit) {
if ("Read limit exceeded".equals(limit.getMessage())) {
LOG.warn("Entry {} exceeds declared size (possible zip bomb), skipping", zipEntry.getName());
continue;
}
throw limit;
} Defensive patterns
Strategy: validation
Validate before calling
// The limit equals the entry's declared uncompressed size.
// If you can read the header, sanity-check the declared size before reading:
long declared = entry.getUncompressedSize();
if (declared < 0 || declared > MAX_ACCEPTABLE_ENTRY_SIZE) {
LOG.warn("Entry {} declares suspicious uncompressed size {}, skipping", entry.getName(), declared);
return;
} Try / catch
try {
bytes = entry.getBytes();
} catch (IllegalStateException e) {
if ("Read limit exceeded".equals(e.getMessage())) {
LOG.warn("Possible zip bomb in entry {}, skipping", entry.getName());
continue;
}
throw e;
} Prevention
- Keep the limited-data-stream guard enabled for untrusted archives (it is the zip-bomb defense).
- Reject archives whose entries declare implausibly large uncompressed sizes up front.
- Treat a Read limit exceeded as a security signal, not a recoverable data error.
When it happens
Trigger: Reading from an InputStream produced by FallbackZipParser when zipSecurity.useLimitedDataStream() is enabled, for an entry whose actual decompressed byte count exceeds entry.getUncompressedSize(). Any single-byte or bulk read past the limit trips addAndCheckPos().
Common situations: A crafted zip-bomb whose compressed data decompresses to more bytes than the (spoofed) uncompressed-size field declares; a legitimately corrupt entry with a wrong size header; an obfuscator/packer that writes inaccurate uncompressed sizes. Also reached during jadx CLI/GUI processing of untrusted APKs where the size limit is on by default.
Related errors
- Entry is encrypted, failed to decompress: {}
- {}
- Failed to open zip: {}
- Failed to process zip file: {}
- Failed to process zip entry: {}
AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14).
Data as JSON: /api/errors/04bdcd00324dacab.
Report an issue: GitHub.