Tencent/tinker · error · DexException

invalid LEB128 sequence

Error message

invalid LEB128 sequence

What it means

Thrown by Leb128.readSignedLeb128 when a signed LEB128 varint still has its continuation bit (0x80) set after 5 bytes. LEB128 encodes at most 32 bits in 5 groups of 7 bits (35 bits, but a 32-bit int needs ≤5); a 6th continuation means the stream is not a valid LEB128 encoding of an int.

Source

Thrown at third-party/aosp-dexutils/src/main/java/com/tencent/tinker/android/dex/Leb128.java:99

    /**
     * Reads an signed integer from {@code in}.
     */
    public static int readSignedLeb128(ByteInput in) {
        int result = 0;
        int cur;
        int count = 0;
        int signBits = -1;

        do {
            cur = in.readByte() & 0xff;
            result |= (cur & 0x7f) << (count * 7);
            signBits <<= 7;
            count++;
        } while (((cur & 0x80) == 0x80) && count < 5);

        if ((cur & 0x80) == 0x80) {
            throw new DexException("invalid LEB128 sequence");
        }

        // Sign extend if appropriate
        if (((signBits >> 1) & result) != 0) {
            result |= signBits;
        }

        return result;
    }

    /**
     * Reads an unsigned leb128 integer from {@code in}.
     */
    public static int readUnsignedLeb128(ByteInput in) {
        int result = 0;
        int cur;
        int count = 0;

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Validate the dex before parsing (checksum + SHA-1 signature in the header, or dexdump) to rule out corruption.
  2. Audit any custom writer that produced the input to ensure it emits canonical, ≤5-byte LEB128.
  3. If reading a structured region, re-derive the start offset — an off-by-N start makes every subsequent varint invalid.
Defensive patterns

Strategy: try-catch

Try / catch

try { int v = Leb128.readSignedLeb128(in); } catch (DexException e) { throw new CorruptDexException(dexName, offset, e); }

Prevention

When it happens

Trigger: Parsing string_ids/type list sizes, ULEB128 fields in class_def, or any signed varint field from a buffer whose position is wrong (usually 1+ bytes off), or from a corrupt/truncated dex where what follows is payload, not a varint.

Common situations: Misaligned reads after a partial skip; dex files truncated by a bad copy or OTA patch; hand-built dex writers emitting >5-byte (over-long, non-canonical) LEB128 encodings that AOSP rejects.

Related errors


AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14). Data as JSON: /api/errors/61120ef1f6180344. Report an issue: GitHub.