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
- Update to the latest jadx; catch-block arg handling has been hardened over releases.
- Isolate the failing class via logs and minimise a reproducer.
- Inspect handler.getArg().getClass() at runtime (temporary logging) to see which InsnArg subtype leaked through, then trace the pass that created it.
- Report to jadx with the reproducer and stack trace.
- 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
- Wrap catch-block emission per method so one bad handler does not abort the class.
- Report obfuscated exception tables that trigger this - they reveal decoder gaps.
- Prefer decompiling per-class with isolation rather than whole-jar batch mode.
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
- Unexpected key in switch:
- Unknown type in literalToString:
- Failed to generate code for class: ${cls.getFullName()}
- Unexpected field type class:
- Can't decode value:
AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14).
Data as JSON: /api/errors/431f4a957774149f.
Report an issue: GitHub.