Tencent/tinker · critical · IllegalStateException

Switch instruction at address/index 0x%x/%d points to the en

Error message

Switch instruction at address/index 0x%x/%d points to the end of the method.

What it means

Thrown while dexlib2's BuilderMutableMethodImplementation verifies switch instructions during conversion of a method's instructions into builder form (used by Tinker when parsing/rewriting dex files). It means a PACKED_SWITCH/SPARSE_SWITCH instruction's branch target offset resolves to the end of the method, where no instruction exists. This indicates a malformed or non-standard dex produced by an aggressive obfuscator/protector or a damaged file.

Source

Thrown at tinker-build/tinker-patch-lib/src/main/java/org/jf/dexlib2/builder/BuilderMutableMethodImplementation.java:474

            }
        }
        return null;
    }

    private void fixInstructions() {
        HashSet<MethodLocation> payloadLocations = Sets.newHashSet();

        for (MethodLocation location : instructionList) {
            BuilderInstruction instruction = location.instruction;
            if (instruction != null) {
                switch (instruction.getOpcode()) {
                    case SPARSE_SWITCH:
                    case PACKED_SWITCH: {
                        MethodLocation targetLocation =
                                ((BuilderOffsetInstruction) instruction).getTarget().getLocation();
                        BuilderInstruction targetInstruction = targetLocation.instruction;
                        if (targetInstruction == null) {
                            throw new IllegalStateException(String.format("Switch instruction at address/index "
                                    + "0x%x/%d points to the end of the method.", location.codeAddress, location.index));
                        }

                        if (targetInstruction.getOpcode() == Opcode.NOP) {
                            targetInstruction = getFirstNonNop(targetLocation.index + 1);
                        }
                        if (targetInstruction == null || !(targetInstruction instanceof BuilderSwitchPayload)) {
                            throw new IllegalStateException(String.format("Switch instruction at address/index "
                                            + "0x%x/%d does not refer to a payload instruction.",
                                    location.codeAddress, location.index));
                        }
                        if ((instruction.opcode == Opcode.PACKED_SWITCH
                                && targetInstruction.getOpcode() != Opcode.PACKED_SWITCH_PAYLOAD)
                                || (instruction.opcode == Opcode.SPARSE_SWITCH
                                        && targetInstruction.getOpcode() != Opcode.SPARSE_SWITCH_PAYLOAD)) {
                            throw new IllegalStateException(String.format("Switch instruction at address/index "
                                            + "0x%x/%d refers to the wrong type of payload instruction.",
                                    location.codeAddress, location.index));

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Upgrade Tinker (and its bundled dexlib2/tinker-patch-lib) to a version that supports the dex producer
  2. Rebuild the base apk without the offending obfuscation/packing step and regenerate the patch
  3. Verify dex integrity (dex checksum/signature) before feeding it to the patch toolchain
  4. If only one class is affected, exclude that dex/class from patching while you investigate
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate dex with a strict parser before Tinker processes it
try {
    org.jf.dexlib2.iface.DexFile df = org.jf.dexlib2.DexFileFactory.loadDexFile(dexFile);
    df.getClasses(); // force full parse
} catch (Exception e) {
    throw new IllegalArgumentException("dex fails pre-parse, skip patching", e);
}

Try / catch

try {
    // dex diff or patch application
} catch (IllegalStateException e) {
    if (e.getMessage() != null && e.getMessage().contains("points to the end of the method")) {
        // reject this dex/patch, alert, do not retry blindly
    } else { throw e; }
}

Prevention

When it happens

Trigger: Loading a dex into BuilderMutableMethodImplementation (e.g., during Tinker patch generation or on-device dex patch applying) where a switch instruction's target Label/offset points past the final instruction of the method.

Common situations: Dex protected by packers/obfuscators (360 Jiagu, Bangcle, DexGuard tricks) that emit unconventional switch payloads; truncated or corrupted dex from a bad download; dex produced by toolchains newer than the dexlib2 bundled with your Tinker version.

Related errors


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