oracle/graal · error · ArithmeticException
Value is larger than 32-bits
Error message
Value is larger than 32-bits
What it means
LEB128.readUnsignedInt decodes an unsigned LEB128 varint and refuses values that do not fit in 32 bits: the 5th 7-bit group must contribute at most 4 bits (bits 28..31). If the 5th byte has any of bits 4-7 set (b & 0xF0 != 0), the encoded value exceeds Integer.MAX_VALUE and an ArithmeticException is thrown. In the CDS reader this indicates a malformed or corrupted variable-length integer in the archive stream.
Source
Thrown at espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/cds/LEB128.java:38
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.truffle.espresso.cds;
import java.util.function.IntConsumer;
import java.util.function.IntSupplier;
public final class LEB128 {
public static int readUnsignedInt(IntSupplier readByte) {
int result = 0;
for (int i = 0;; ++i) {
byte b = (byte) readByte.getAsInt();
result |= (b & 0x7F) << (i * 7);
// The first 4 groups of 7 bits are guaranteed to fit (4 * 7 = 28 bits).
// That leaves room for only the 4 low-order bits from the 5th group (which has index 4)
if (i == 4 && (b & 0xF0) != 0) {
throw new ArithmeticException("Value is larger than 32-bits");
}
if ((b & 0x80) == 0) {
return result;
}
}
}
public static void writeUnsignedInt(IntConsumer writeByte, int value) {
int tmp = value;
do {
int b = tmp & 0x7F;
tmp >>>= 7;
if (tmp != 0) {
b |= 0x80;
}
writeByte.accept(b & 0xFF);
} while (tmp != 0);
}View on GitHub (pinned to a66e9ccd1d)
Solutions
- Regenerate the CDS archive from scratch with the current Espresso version so the varint stream is self-consistent.
- Verify archive integrity (file size, checksum) after copying/downloading it; a truncated tail commonly lands exactly here.
- Check that the archive was produced by the same CDSArchiveFormat version (see the header version checks in Reader.readHeader).
- If you call LEB128.readUnsignedInt on your own data, ensure the encoder (LEB128.writeUnsignedInt) only ever receives values that fit in an int.
Example fix
// before int v = LEB128.readUnsignedInt(in); // throws for values >= 2^32 // after // encode with the matching int-width writer LEB128.writeUnsignedInt(out, value); // value always < 2^31
Defensive patterns
Strategy: validation
Validate before calling
static boolean fitsUnsigned32(long v) { return (v & 0xFFFFFFFFL) == v && (v >>> 28) == 0 || Long.compareUnsigned(v, 1L << 28) < 0 || (v >>> 28) <= 0xF; }
// simpler: check before writing
if (Integer.toUnsignedLong(value) >= (1L << 32)) throw new IllegalArgumentException("value exceeds 32-bit unsigned"); Try / catch
try {
int v = LEB128.readUnsignedInt(readByte);
} catch (ArithmeticException e) {
// stream corrupt or 64-bit varint: fail the whole read, do not resync
throw new IllegalArgumentException("Corrupt varint in stream", e);
} Prevention
- Only pair readUnsignedInt with writeUnsignedInt (never a 64-bit varint writer).
- Checksum CDS archives after generation and verify before reading to catch truncation.
- Never attempt to resync a LEB128 stream mid-value; treat any ArithmeticException as fatal corruption.
When it happens
Trigger: Any CDS archive read that decodes a varint (lengths, refIds, counts) where the byte stream is truncated, shifted, or corrupt so that the 5th continuation group carries high bits. Also any direct use of LEB128.readUnsignedInt on data encoded by a 64-bit varint writer (values >= 2^32).
Common situations: Truncated CDS archive (partial download, interrupted write, disk filling up mid-write); archive written by a different/newer format that encodes 64-bit lengths; byte-order or offset bug in custom code feeding the decoder; archive regenerated on a machine with a much larger heap producing sizes > 2GB encoded as 64-bit.
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/41c35db2addfdd8b.
Report an issue: GitHub.