skylot/jadx · error · RuntimeException

Failed to read bytes for entry

Error message

Failed to read bytes for entry: {}

What it means

Thrown by the fallback zip parser (Java built-in ZipFile based) when fully reading an entry's decompressed bytes fails. getBytes() wraps the underlying InputStream in a LimitedInputStream and calls readAllBytes(); any IOException/DataFormatException/IllegalStateException from opening the stream, the read, or the size guard is caught and rethrown as a RuntimeException that names the offending entry. The original cause is attached so the real failure (CRC error, truncation, encryption) is inspectable via getCause().

Solutions

  1. Inspect getCause() on the thrown RuntimeException to identify whether it is CRC failure, truncation, the read-limit guard (LimitedInputStream), or a decrypt error.
  2. If the cause is a read-limit or size mismatch, treat the archive as untrusted/suspect and re-acquire it from a trusted source.
  3. If the cause is encryption, jadx does not support decrypting protected entries; supply an unencrypted archive or skip the entry.
  4. Wrap the call in a try/catch and skip the failing entry so the rest of the archive still processes.

Example fix

// before
byte[] data = entry.getBytes();
process(data);

// after
byte[] data;
try {
    data = entry.getBytes();
} catch (RuntimeException e) {
    LOG.warn("Skipping unreadable entry {}: {}", entry.getName(), e.getCause());
    continue;
}
process(data);
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate the entry is readable by checking the underlying zip is still open
// and the entry is not encrypted. There is no cheap 'canRead' probe, so validate
// what you can:
if (zipEntry.isDirectory()) {
    return; // directories have no bytes
}

Try / catch

try {
    byte[] data = zipEntry.getBytes();
} catch (RuntimeException e) {
    Throwable cause = e.getCause(); // IOException, DataFormatException, IllegalStateException
    LOG.warn("Skipping entry {}: {}", zipEntry.getName(), cause);
    continue;
}

Prevention

When it happens

Trigger: Calling IZipEntry.getBytes() / FallbackZipParser.getBytes() on a FallbackZipEntry whose underlying ZipFile.getInputStream() throws, or whose readAllBytes() hits an IOException, or whose LimitedInputStream trips the read-limit guard. This code path runs when the custom JadxZipParser could not handle the archive and jadx switched to the fallback parser.

Common situations: A corrupted or truncated APK/JAR/WAR where the central directory points at damaged compressed data; an entry that was partially written during an interrupted build; a crafted zip with a deliberately small declared uncompressed size; an encrypted entry the standard ZipFile cannot decrypt; disk I/O errors on the temp file holding the archive.

Related errors


AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14). Data as JSON: /api/errors/7337ce32e25e78eb. Report an issue: GitHub.

Appendix: source

Thrown at jadx-commons/jadx-zip/src/main/java/jadx/zip/fallback/FallbackZipParser.java:78

			return new ZipContent(this, list);
		} catch (Exception e) {
			throw new FallbackException("Error opening zip file: " + file.getAbsolutePath(), e);
		}
	}

	private boolean isValidEntry(IZipEntry zipEntry) {
		boolean validEntry = zipSecurity.isValidEntry(zipEntry);
		if (!validEntry) {
			LOG.warn("Zip entry '{}' is invalid and excluded from processing", zipEntry);
		}
		return validEntry;
	}

	public byte[] getBytes(FallbackZipEntry entry) {
		try (InputStream is = getEntryStream(entry)) {
			return is.readAllBytes();
		} catch (Exception e) {
			throw new RuntimeException("Failed to read bytes for entry: " + entry.getName(), e);
		}
	}

	public InputStream getInputStream(FallbackZipEntry entry) {
		try {
			return getEntryStream(entry);
		} catch (Exception e) {
			throw new RuntimeException("Failed to open input stream for entry: " + entry.getName(), e);
		}
	}

	private InputStream getEntryStream(FallbackZipEntry entry) throws IOException {
		InputStream entryStream = zipFile.getInputStream(entry.getZipEntry());
		InputStream stream;
		if (useLimitedDataStream) {
			stream = new LimitedInputStream(entryStream, entry.getUncompressedSize());
		} else {
			stream = entryStream;

View on GitHub (pinned to e738a26571)