skylot/jadx · error · JadxArgsValidateException
Unknown option: {}
Error message
Unknown option: {} What it means
Thrown by RegisterObserver.getRegListEntry when the runtime register number it is asked to look up falls outside the smali register list (regList.get(regNum) raises IndexOutOfBoundsException). regList is built in merge() from the smali registers and sorted by getRuntimeRegNum(); the code then indexes it positionally with a device-reported RuntimeVarInfo.getRegNum(). The error fires when the device's register numbering does not line up 1:1 with the smali register positions (a register number >= list size, or a gap in the runtime layout). The thrown RuntimeException bundles regNum, list size, device info and method id for triage.
Source
Thrown at jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java:434
LogHelper.applyLogLevels();
}
public boolean process(JCommanderWrapper jcw) {
if (jcw.processCommands()) {
return false;
}
if (printHelp) {
jcw.printUsage();
return false;
}
if (printVersion) {
System.out.println(JadxDecompiler.getVersion());
return false;
}
// unknown options added to 'files', run checks
for (String fileName : files) {
if (fileName.startsWith("-")) {
throw new JadxArgsValidateException("Unknown option: " + fileName);
}
}
return true;
}
private static void printFilesAndDirs(String defaultConfigFileName) {
System.out.println("Files and directories used by jadx:");
System.out.println(" - default config file: " + JadxCommonFiles.getConfigDir().resolve(defaultConfigFileName).toAbsolutePath());
System.out.println(" - config directory: " + JadxCommonFiles.getConfigDir().toAbsolutePath());
System.out.println(" - cache directory: " + JadxCommonFiles.getCacheDir().toAbsolutePath());
System.out.println(" - temp directory: " + JadxTempFiles.getTempRootDir().getParent().toAbsolutePath());
}
public void verify() {
if (threadsCount <= 0) {
throw new JadxArgsValidateException("Threads count must be positive, got: " + threadsCount);
}
}View on GitHub (pinned to e738a26571)
Solutions
- Try a different debuggable build of the target APK (non-minified, non-obfuscated) so the smali register set and the runtime variable table agree.
- Switch the device/ART adapter if the build offers more than one (ArtAdapter selection); the printed ArtAdapter + Android version in the message tells you which one mismatched.
- Upgrade jadx to a newer version where RegisterObserver handles register gaps (recent builds skip out-of-range RuntimeVarInfo instead of indexing blindly).
- If you control jadx, guard rt.getRegNum() against regList.size() in merge() and skip unmappable registers instead of letting getRegListEntry throw.
Example fix
// before
final SmaliRegisterMapping smaliRegMapping = adapter.getRegListEntry(rt.getRegNum());
// after
int regNum = rt.getRegNum();
if (regNum < 0 || regNum >= adapter.regList.size()) {
LOG.warn("Skip runtime var {}: out of smali register range (size {})", regNum, adapter.regList.size());
continue;
}
final SmaliRegisterMapping smaliRegMapping = adapter.regList.get(regNum); Defensive patterns
Strategy: validation
Validate before calling
// Before calling getRegListEntry(rt.getRegNum()) in merge() or getInfo(runtimeNum,...):
int regNum = rt.getRegNum();
if (regNum < 0 || regNum >= regList.size()) {
LOG.warn("Runtime reg {} outside smali register list (size {}) for {}", regNum, regList.size(), mthFullID);
continue; // or return null
}
SmaliRegisterMapping entry = regList.get(regNum); Try / catch
// RegisterObserver is internal; callers cannot meaningfully recover.
// Prefer the bounds validation above. If you must wrap it:
try {
observer.getInfo(runtimeNum, codeOffset);
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().contains("Register") && e.getMessage().contains("does not exist")) {
LOG.warn("Register mapping miss, skipping", e);
return null;
}
throw e;
} Prevention
- Use a debuggable, non-obfuscated APK so the runtime variable table matches the smali register set.
- Match the ArtAdapter to the target Android/ART version; the error message prints both for triage.
- Keep jadx current; later builds tolerate register gaps instead of indexing blindly.
- Treat a repeat of this error on one device as an adapter-selection problem, not a code bug.
When it happens
Trigger: Calling RegisterObserver.merge(rtRegs, smaliRegs, ...) where a RuntimeVarInfo reports getRegNum() that is >= smaliRegs.size(); or calling getInfo(runtimeNum, codeOffset) with a runtime register number larger than the populated regList. Happens while the debugger resolves live variable info for a suspended frame, i.e. the moment DebugController asks RegisterObserver for a register that the device claims exists but that the smali view does not contain.
Common situations: Debugging optimized/obfuscated DEX where ART's variable table references registers the smali disassembly does not expose; a device/ART version whose register numbering differs from what the selected ArtAdapter expects (the message prints ArtAdapter class + Android version for exactly this reason); stepping into a synthetic or inlined method whose register layout mismatches jadx's smali model.
Related errors
- Threads count must be positive, got: {}
- '{}' is unknown for parameter {}, possible values are {}
- Input class can't be saved by current jadx settings (marked
- Found {} classes, single class output can't be used
- Class decompilation failed
AI-assisted analysis of skylot/jadx@e738a26571 (2026-08-14).
Data as JSON: /api/errors/f560323c0071ba0a.
Report an issue: GitHub.