skylot/jadx · error · JadxArgsValidateException
Threads count must be positive, got: {}
Error message
Threads count must be positive, got: {} What it means
Thrown by DebugController.castType(ArgType) when converting a register's jadx ArgType to the RuntimeType accepted by the live debugger. The method only maps six kinds: INT, STRING, LONG, FLOAT, DOUBLE and OBJECT. Any other ArgType (BOOLEAN, BYTE, CHAR, SHORT, ARRAY, VOID, multi-dim arrays, etc.) reaches the final throw. It signals that the register the user tried to read/modify holds a type this debugger build cannot represent over JDWP, not a corrupt state.
Source
Thrown at jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java:450
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);
}
}
private static <T extends JadxCLIArgs> void saveConfig(T argsObj, @Nullable JadxConfigAdapter<T> configAdapter) {
if (configAdapter == null) {
throw new JadxRuntimeException("Config adapter set to null, can't save config");
}
configAdapter.useConfigRef(argsObj.saveConfig);
configAdapter.save(argsObj);
System.out.println("Config saved to " + configAdapter.getConfigPath().toAbsolutePath());
}
public JadxArgs toJadxArgs() {
JadxArgs args = new JadxArgs();
args.setInputFiles(files.stream().map(FileUtils::toFile).collect(Collectors.toList()));
args.setOutDir(FileUtils.toFile(outDir));
args.setOutDirSrc(FileUtils.toFile(outDirSrc));
args.setOutDirRes(FileUtils.toFile(outDirRes));View on GitHub (pinned to e738a26571)
Solutions
- Do not attempt to modify registers of unsupported primitive widths (boolean/byte/char/short/array); the debugger only edits int, long, float, double, String and object values.
- If you must change such a value, widen it at the source (rebuild the APK) or step to a point where the value lives in an int register.
- In jadx itself, extend castType to map the missing primitive kinds to their JDWP equivalents (BYTE->RuntimeType.BYTE etc.) and ensure RuntimeType supports them.
- Guard the UI path so unsupported types are disabled rather than reaching castType.
Example fix
// before
private RuntimeType castType(ArgType type) {
if (type == ArgType.INT) return RuntimeType.INT;
// ... STRING, LONG, FLOAT, DOUBLE, OBJECT
throw new JadxRuntimeException("Unexpected type: " + type);
}
// after
private RuntimeType castType(ArgType type) {
if (type == ArgType.INT) return RuntimeType.INT;
if (type == ArgType.STRING) return RuntimeType.STRING;
if (type == ArgType.LONG) return RuntimeType.LONG;
if (type == ArgType.FLOAT) return RuntimeType.FLOAT;
if (type == ArgType.DOUBLE) return RuntimeType.DOUBLE;
if (type == ArgType.OBJECT) return RuntimeType.OBJECT;
if (type == ArgType.BOOLEAN) return RuntimeType.BOOLEAN;
if (type == ArgType.BYTE) return RuntimeType.BYTE;
if (type == ArgType.CHAR) return RuntimeType.CHAR;
if (type == ArgType.SHORT) return RuntimeType.SHORT;
throw new JadxRuntimeException("Unsupported edit type: " + type);
} Defensive patterns
Strategy: type-guard
Validate before calling
// Guard before invoking the modify path that calls castType(ArgType):
private static final Set<ArgType> EDITABLE = EnumSet.noneOf(ArgType.class);
static {
EDITABLE.add(ArgType.INT); EDITABLE.add(ArgType.STRING); EDITABLE.add(ArgType.LONG);
EDITABLE.add(ArgType.FLOAT); EDITABLE.add(ArgType.DOUBLE); EDITABLE.add(ArgType.OBJECT);
}
boolean isEditableType(ArgType t) { return EDITABLE.contains(t); }
// call: if (!isEditableType(type)) { disableEditUi(); return; } Type guard
static boolean isSupportedEditType(ArgType type) {
return type == ArgType.INT || type == ArgType.STRING || type == ArgType.LONG
|| type == ArgType.FLOAT || type == ArgType.DOUBLE || type == ArgType.OBJECT;
} Try / catch
// UI layer: degrade gracefully instead of crashing the debug session
RuntimeType rt;
try {
rt = castType(type);
} catch (JadxRuntimeException e) {
if (e.getMessage().startsWith("Unexpected type")) {
LOG.info("Edit unsupported for type {}", type);
return false;
}
throw e;
} Prevention
- Disable the edit control for registers whose ArgType is not in the supported set.
- If you extend castType to new primitives, also extend checkType and the UI coercion.
- Keep castType's handled set in sync with the runtime types SmaliDebugger.setValueSync accepts.
When it happens
Trigger: Invoking register value inspection/modification (modifyValueInternal path) on a register whose ArgType is BOOLEAN, BYTE, CHAR, SHORT, ARRAY or any type outside the six handled branches. castType(type) is called to derive the RuntimeType sent to SmaliDebugger.setValueSync / value fetch.
Common situations: Editing a boolean or char field/register in the debugger UI; stopping on a method that uses byte/short locals; obfuscated code that widens primitive types. The debugger simply does not support editing those primitive widths.
Related errors
- Unknown option: {}
- '{}' 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/00255a0386a60298.
Report an issue: GitHub.