pxb1988/dex2jar · error · RuntimeException

can't cast to

Error message

can't cast  to 

What it means

Thrown by InvocationWeaver.visitMethodInsn while rewriting an INVOKE* instruction: if the original method returns a value (orgRet is not void) but the mapped replacement returns void (nRet is void), there is no value to leave on the stack for the consuming instruction, so the weaver refuses with 'can't cast <newRet> to <orgRet>'.

Solutions

  1. Update the mapping so the replacement method returns a type compatible with the original method's return type.
  2. Point mapTo at an overload that still produces the value instead of a void variant.
  3. If the value is truly unneeded, rewrite the original call sites too (or remove the consuming instructions) rather than mapping to a void method.

Example fix

// before: mapping original 'Ljava/lang/String; get()' to '()V'
mapTo = Lcom/X;->g()V
// after
mapTo = Lcom/X;->g()Ljava/lang/String;
Defensive patterns

Strategy: validation

Validate before calling

// validate each mapping before weaving
Type orgRet = Type.getReturnType(original.desc);
Type nRet = Type.getReturnType(mapTo.desc);
if (orgRet.getSort() != Type.VOID && nRet.getSort() == Type.VOID) {
    throw new IllegalArgumentException("mapping drops return value: " + original + " -> " + mapTo);
}

Try / catch

try {
    new InvocationWeaver(cfg).weave();
} catch (RuntimeException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("can't cast")) {
        throw new IllegalStateException("Mapping maps a value-returning method to a void method; fix mapTo", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: A method mapping redirects a call to a replacement method that returns void while the original call site expects a return value (e.g. replacing 'String get()' with a void method), encountered during dex/jar weaving.

Common situations: Config mismatches after refactoring the target API — the replacement method signature was changed to return void, or the mapping's descriptor was hand-edited incorrectly.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of pxb1988/dex2jar@b5bda4fb49 (2026-09-08). Data as JSON: /api/errors/65770af2fbbf6255. Report an issue: GitHub.

Appendix: source

Thrown at dex-tools/src/main/java/com/googlecode/d2j/tools/jar/InvocationWeaver.java:435

                return  mv==null?null: new ReplaceMethodVisitor(mv);
            }
            class ReplaceMethodVisitor extends MethodVisitor {
                public ReplaceMethodVisitor(MethodVisitor mv) {
                    super(ASM9, mv);
                }

                @Override
                public void visitMethodInsn(int opcode, String owner, String name, String desc,
                                            boolean isInterface) {
                    MtdInfo mapTo = findTargetMethod("L" + owner + ";", name, desc);
                    if (mapTo != null) {
                        boolean isStatic = opcode == INVOKESTATIC;
                        Type orgRet = Type.getReturnType(desc);
                        Type[] orgArgs = Type.getArgumentTypes(desc);
                        Type nRet = Type.getReturnType(mapTo.desc);
                        Type[] nArgs = Type.getArgumentTypes(mapTo.desc);
                        if (orgRet.getSort() != Type.VOID && nRet.getSort() == Type.VOID) {
                            throw new RuntimeException("can't cast " + nRet + " to " + orgRet);
                        }

                        if (nArgs.length == 1 && nArgs[0].getDescriptor().equals(invocationInterfaceDesc)) {
                            MtdInfo t = new MtdInfo();
                            t.owner = "L" + owner + ";";
                            t.name = name;
                            t.desc = desc;
                            MtdInfo n = newMethodA(opcode, t, mapTo);
                            super.visitMethodInsn(INVOKESTATIC, clzName, n.name, n.desc, isInterface);
                        } else { // simple replace
                            // checking for invalid replace
                            if (isStatic) {
                                if (!Arrays.deepEquals(orgArgs, nArgs)) {
                                    throw new RuntimeException("arguments not equal: " + owner + "." + name + desc
                                            + " <> " + mapTo.owner + "." + mapTo.name + mapTo.desc);
                                }
                            } else {
                                if (nArgs.length != orgArgs.length + 1) {

View on GitHub (pinned to b5bda4fb49)