skylot/jadx · error · JadxRuntimeException
Unexpected missing predecessor for block: {}
Error message
Unexpected missing predecessor for block: {} What it means
Thrown during exception handler edge setup in block splitting. For each instruction with an EXC_CATCH attribute, jadx connects the handler block to the appropriate predecessor. If the block containing the throwing instruction has zero predecessors, the connection point is undefined.
Source
Thrown at jadx-core/src/main/java/jadx/core/dex/visitors/blocks/BlockSplitter.java:280
* This temporary connection is necessary to build close to a final dominator tree.
* Will be used and removed in {@code jadx.core.dex.visitors.blocks.BlockExceptionHandler}
*/
private static void addTempConnectionsForExcHandlers(MethodNode mth, Map<Integer, BlockNode> blocksMap) {
if (mth.isNoExceptionHandlers()) {
return;
}
for (BlockNode block : mth.getBasicBlocks()) {
for (InsnNode insn : block.getInstructions()) {
CatchAttr catchAttr = insn.get(AType.EXC_CATCH);
if (catchAttr == null) {
continue;
}
for (ExceptionHandler handler : catchAttr.getHandlers()) {
BlockNode handlerBlock = getBlock(handler.getHandlerOffset(), blocksMap);
if (!handlerBlock.contains(AType.TMP_EDGE)) {
List<BlockNode> preds = block.getPredecessors();
if (preds.isEmpty()) {
throw new JadxRuntimeException("Unexpected missing predecessor for block: " + block);
}
BlockNode start = preds.size() == 1 ? preds.get(0) : block;
if (!start.getSuccessors().contains(handlerBlock)) {
connect(start, handlerBlock);
handlerBlock.addAttr(new TmpEdgeAttr(start));
}
}
}
}
}
}
private static void setupExitConnections(MethodNode mth) {
BlockNode exitBlock = mth.getExitBlock();
for (BlockNode block : mth.getBasicBlocks()) {
if (block.getSuccessors().isEmpty() && block != exitBlock) {
connect(block, exitBlock);
if (BlockUtils.checkLastInsnType(block, InsnType.RETURN)) {View on GitHub (pinned to e738a26571)
Solutions
- Update jadx — exception edge setup is frequently hardened against edge cases
- Report the APK as a jadx issue
- Verify the DEX exception table with baksmali to check for malformed entries
- Decompile classes individually to isolate the problem method
Defensive patterns
Strategy: try-catch
Try / catch
try {
jadxDecompiler.load();
jadxDecompiler.save();
} catch (JadxRuntimeException e) {
if (e.getMessage().contains("Unexpected missing predecessor")) {
LOG.warn("Exception edge setup inconsistency, skipping problematic class");
for (ClassNode cls : jadxDecompiler.getClasses()) {
try {
jadxDecompiler.decompileClass(cls);
} catch (JadxRuntimeException ex) {
LOG.warn("Skipped: {}", cls.getFullName());
}
}
} else {
throw e;
}
} Prevention
- Verify DEX exception tables with baksmali for malformed entries
- Keep jadx updated — exception edge setup is frequently hardened
- Report the APK as a jadx issue
- Decompile classes individually to isolate the problematic method
When it happens
Trigger: A block containing an instruction with EXC_CATCH attribute has an empty predecessors list. The code needs at least one predecessor to establish the exception edge, so it throws when preds.isEmpty() is true.
Common situations: Malformed CFG where exception-throwing instructions exist in blocks with no incoming edges; block splitting produced a disconnected block; obfuscated bytecode with unusual exception table entries pointing to unreachable code.
Related errors
- Missing block: {}
- Try blocks wrapping queue limit reached! Please report as an
- Failed to find top block for try-catch from: {}
- Same handlers in try block: {}
- Unreachable block: {}
AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14).
Data as JSON: /api/errors/5b7cc2c8a3737f71.
Report an issue: GitHub.