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

  1. Update jadx — exception edge setup is frequently hardened against edge cases
  2. Report the APK as a jadx issue
  3. Verify the DEX exception table with baksmali to check for malformed entries
  4. 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

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


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