skylot/jadx · error · JadxArgsValidateException
Found {} classes, single class output can't be used
Error message
Found {} classes, single class output can't be used What it means
Thrown by SmaliDebugger.attach when an IOException occurs during the whole attach sequence: opening the TCP socket to host:port, running initJDWP (handshake + suspend + id-sizes), or constructing/starting the debugger. It is the top-level wrapper that says the JDWP connection to the debugged app could not be established, with the original IOException chained as the cause.
Source
Thrown at jadx-cli/src/main/java/jadx/cli/SingleClassMode.java:56
}
if (clsForProcess.contains(AFlag.DONT_GENERATE)) {
throw new JadxArgsValidateException("Input class can't be saved by current jadx settings (marked as DONT_GENERATE)");
}
if (clsForProcess.isInner()) {
clsForProcess = clsForProcess.getTopParentClass();
LOG.warn("Input class is inner, parent class will be saved: {}", clsForProcess.getFullName());
}
} else {
// singleClassOutput is set
// expect only one class to be loaded
List<ClassNode> classes = jadx.getRoot().getClasses().stream()
.filter(c -> !c.isInner() && !c.contains(AFlag.DONT_GENERATE))
.collect(Collectors.toList());
int size = classes.size();
if (size == 1) {
clsForProcess = classes.get(0);
} else {
throw new JadxArgsValidateException("Found " + size + " classes, single class output can't be used");
}
}
ICodeInfo codeInfo;
try {
codeInfo = clsForProcess.decompile();
} catch (Exception e) {
throw new JadxRuntimeException("Class decompilation failed", e);
}
String fileExt = SaveCode.getFileExtension(jadx.getRoot());
File out;
if (singleClassOutput == null) {
out = new File(jadx.getArgs().getOutDirSrc(), clsForProcess.getClassInfo().getAliasFullPath() + fileExt);
} else {
if (singleClassOutput.endsWith(fileExt)) {
// treat as file name
out = new File(singleClassOutput);
} else {
// treat as directoryView on GitHub (pinned to e738a26571)
Solutions
- Re-run the ADB jdwp port forward for the target process id (`adb forward tcp:<port> jdwp:<pid>`) and retry attach.
- Confirm the app is installed as a debuggable build and is still running before attaching.
- Verify the device is online (`adb devices`) and only one debugger attaches at a time.
- Inspect the chained IOException cause to distinguish connection-refused vs timeout vs handshake failure, then address that specific layer.
Example fix
// before
SmaliDebugger dbg = SmaliDebugger.attach(host, port, listener);
// after
SmaliDebugger dbg;
try {
dbg = SmaliDebugger.attach(host, port, listener);
} catch (SmaliDebuggerException e) {
// cause reveals refused vs timeout vs handshake; re-establish the jdwp forward then retry once
ensureJdwpForward(packageName, port);
dbg = SmaliDebugger.attach(host, port, listener);
} Defensive patterns
Strategy: retry
Validate before calling
// Verify the jdwp forward and target liveness before calling attach:
boolean ok = ensureJdwpForward(deviceSerial, packageName, port) && isProcessAlive(packageName);
if (!ok) {
throw new IllegalStateException("jdwp forward/process not ready on port " + port);
}
return SmaliDebugger.attach(host, port, listener); Try / catch
SmaliDebugger dbg = null;
for (int attempt = 0; attempt < 2 && dbg == null; attempt++) {
try {
dbg = SmaliDebugger.attach(host, port, listener);
} catch (SmaliDebuggerException e) {
Throwable cause = e.getCause();
if (cause instanceof java.net.SocketTimeoutException || cause instanceof java.net.ConnectException) {
ensureJdwpForward(packageName, port); // re-forward then retry once
continue;
}
throw e; // handshake/protocol errors are not transient
}
}
if (dbg == null) throw new SmaliDebuggerException("Attach failed after retry"); Prevention
- Always set up `adb forward tcp:<port> jdwp:<pid>` immediately before attach.
- Attach to a debuggable build only, while the process is alive.
- Ensure no second debugger grabs the single jdwp session.
- Read the chained IOException cause to pick the right remedy (refused vs timeout vs handshake).
When it happens
Trigger: Calling SmaliDebugger.attach(host, port, suspendListener) when the socket cannot connect or the JDWP setup I/O fails: connection refused (nothing listening on the ADB-forwarded port), connect timeout (device unreachable), stream read/write error, or initJDWP throwing. The 5-second socket timeout (setSoTimeout(5000)) makes slow/unresponsive targets surface here quickly.
Common situations: Wrong ADB jdwp port or forgot `adb forward tcp:port jdwp:pid`; the target app is not debuggable (android:debuggable=false) so no jdwp thread exists; device disconnected/offline; app already terminated before attach; another debugger already attached to the same process; firewall/USB transport issue.
Related errors
- Failed to load config file: {}
- Failed to save config file: {}
- Class decompilation failed
- Input class can't be saved by current jadx settings (marked
- Unknown option: {}
AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14).
Data as JSON: /api/errors/7dde1563e3388fc2.
Report an issue: GitHub.