skylot/jadx · error · JadxRuntimeException

Unexpected arg type in catch block:

Error message

Unexpected arg type in catch block: 

What it means

Thrown by RegionGen when emitting the exception variable of a catch block. The handler's argument must be either a RegisterArg (the normal case, backed by an SSA variable) or a NamedArg. If the exception arg is some other InsnArg subtype the codegen cannot name it and aborts. This is a late-stage codegen invariant failure.

Source

Thrown at jadx-core/src/main/java/jadx/core/codegen/RegionGen.java:376

			while (it.hasNext()) {
				code.add(" | ");
				useClass(code, it.next());
			}
		}
		code.add(' ');
		InsnArg arg = handler.getArg();
		if (arg == null) {
			code.add("unknown"); // throwing exception is too late at this point
		} else if (arg instanceof RegisterArg) {
			SSAVar ssaVar = ((RegisterArg) arg).getSVar();
			if (code.isMetadataSupported()) {
				code.attachDefinition(VarNode.get(mth, ssaVar));
			}
			code.add(mgen.getNameGen().assignArg(ssaVar.getCodeVar()));
		} else if (arg instanceof NamedArg) {
			code.add(mgen.getNameGen().assignNamedArg((NamedArg) arg));
		} else {
			throw new JadxRuntimeException("Unexpected arg type in catch block: " + arg + ", class: " + arg.getClass().getSimpleName());
		}
		code.add(") {");

		InsnCodeOffset.attach(code, handler.getHandlerOffset());
		CodeGenUtils.addCodeComments(code, mth, handler.getHandlerBlock());

		makeRegionIndent(code, region);
	}
}

View on GitHub (pinned to e738a26571)

Solutions

  1. Update to the latest jadx; catch-block arg handling has been hardened over releases.
  2. Isolate the failing class via logs and minimise a reproducer.
  3. Inspect handler.getArg().getClass() at runtime (temporary logging) to see which InsnArg subtype leaked through, then trace the pass that created it.
  4. Report to jadx with the reproducer and stack trace.
  5. Workaround: skip decompilation of the affected class with class filters so the batch finishes.

Example fix

// before
} else if (arg instanceof NamedArg) {
    code.add(mgen.getNameGen().assignNamedArg((NamedArg) arg));
} else {
    throw new JadxRuntimeException("Unexpected arg type in catch block: " + arg + ", class: " + arg.getClass().getSimpleName());
}

// after (defensive fallback name instead of aborting)
} else {
    LOG.warn("Unexpected arg type in catch block: {} ({}); using fallback name", arg, arg.getClass().getSimpleName());
    code.add("e");
}
Defensive patterns

Strategy: type-guard

Validate before calling

InsnArg arg = handler.getArg();
if (!(arg instanceof RegisterArg) && !(arg instanceof NamedArg)) {
    LOG.warn("Unexpected catch arg type {} in {}, using fallback", arg.getClass(), mth);
}

Type guard

static boolean isSupportedCatchArg(InsnArg arg) {
    return arg instanceof RegisterArg || arg instanceof NamedArg;
}

Try / catch

try {
    regionGen.makeCatchBlock(code, handler, region);
} catch (JadxRuntimeException e) {
    LOG.warn("Catch codegen failed in {}: {}", mth, e.getMessage());
    code.add("/* catch block omitted */");
}

Prevention

When it happens

Trigger: Decompiling bytecode whose exception handler argument was rewritten during type/name inference into an unexpected InsnArg subclass (e.g. an InsnArg wrap, a LiteralArg, or an uninitialised arg). Common with synthetic/obfuscated exception tables or after aggressive optimisation passes mutate handler args.

Common situations: Obfuscated apps with unusual exception handlers; malformed DEX; intermediate passes that swap a handler arg type; edge cases around multi-catch or TryWithResources desugaring. Rare on clean inputs.

Related errors


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