{"record":{"id":"00255a0386a60298","repo":"skylot/jadx","slug":"threads-count-must-be-positive-got","errorCode":null,"errorMessage":"Threads count must be positive, got: {}","messagePattern":"Threads count must be positive, got: (.+?)","errorType":"validation","errorClass":"JadxArgsValidateException","httpStatus":null,"severity":"error","filePath":"jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java","lineNumber":450,"sourceCode":"\t\tfor (String fileName : files) {\n\t\t\tif (fileName.startsWith(\"-\")) {\n\t\t\t\tthrow new JadxArgsValidateException(\"Unknown option: \" + fileName);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static void printFilesAndDirs(String defaultConfigFileName) {\n\t\tSystem.out.println(\"Files and directories used by jadx:\");\n\t\tSystem.out.println(\" - default config file: \" + JadxCommonFiles.getConfigDir().resolve(defaultConfigFileName).toAbsolutePath());\n\t\tSystem.out.println(\" - config directory:    \" + JadxCommonFiles.getConfigDir().toAbsolutePath());\n\t\tSystem.out.println(\" - cache directory:     \" + JadxCommonFiles.getCacheDir().toAbsolutePath());\n\t\tSystem.out.println(\" - temp directory:      \" + JadxTempFiles.getTempRootDir().getParent().toAbsolutePath());\n\t}\n\n\tpublic void verify() {\n\t\tif (threadsCount <= 0) {\n\t\t\tthrow new JadxArgsValidateException(\"Threads count must be positive, got: \" + threadsCount);\n\t\t}\n\t}\n\n\tprivate static <T extends JadxCLIArgs> void saveConfig(T argsObj, @Nullable JadxConfigAdapter<T> configAdapter) {\n\t\tif (configAdapter == null) {\n\t\t\tthrow new JadxRuntimeException(\"Config adapter set to null, can't save config\");\n\t\t}\n\t\tconfigAdapter.useConfigRef(argsObj.saveConfig);\n\t\tconfigAdapter.save(argsObj);\n\t\tSystem.out.println(\"Config saved to \" + configAdapter.getConfigPath().toAbsolutePath());\n\t}\n\n\tpublic JadxArgs toJadxArgs() {\n\t\tJadxArgs args = new JadxArgs();\n\t\targs.setInputFiles(files.stream().map(FileUtils::toFile).collect(Collectors.toList()));\n\t\targs.setOutDir(FileUtils.toFile(outDir));\n\t\targs.setOutDirSrc(FileUtils.toFile(outDirSrc));\n\t\targs.setOutDirRes(FileUtils.toFile(outDirRes));","sourceCodeStart":432,"sourceCodeEnd":468,"githubUrl":"https://github.com/skylot/jadx/blob/e738a26571d02919f01df40de93bc9a44dee4e18/jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java#L432-L468","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nprivate RuntimeType castType(ArgType type) {\n    if (type == ArgType.INT) return RuntimeType.INT;\n    // ... STRING, LONG, FLOAT, DOUBLE, OBJECT\n    throw new JadxRuntimeException(\"Unexpected type: \" + type);\n}\n\n// after\nprivate RuntimeType castType(ArgType type) {\n    if (type == ArgType.INT) return RuntimeType.INT;\n    if (type == ArgType.STRING) return RuntimeType.STRING;\n    if (type == ArgType.LONG) return RuntimeType.LONG;\n    if (type == ArgType.FLOAT) return RuntimeType.FLOAT;\n    if (type == ArgType.DOUBLE) return RuntimeType.DOUBLE;\n    if (type == ArgType.OBJECT) return RuntimeType.OBJECT;\n    if (type == ArgType.BOOLEAN) return RuntimeType.BOOLEAN;\n    if (type == ArgType.BYTE) return RuntimeType.BYTE;\n    if (type == ArgType.CHAR) return RuntimeType.CHAR;\n    if (type == ArgType.SHORT) return RuntimeType.SHORT;\n    throw new JadxRuntimeException(\"Unsupported edit type: \" + type);\n}","handlingStrategy":"type-guard","validationCode":"// Guard before invoking the modify path that calls castType(ArgType):\nprivate static final Set<ArgType> EDITABLE = EnumSet.noneOf(ArgType.class);\nstatic {\n    EDITABLE.add(ArgType.INT); EDITABLE.add(ArgType.STRING); EDITABLE.add(ArgType.LONG);\n    EDITABLE.add(ArgType.FLOAT); EDITABLE.add(ArgType.DOUBLE); EDITABLE.add(ArgType.OBJECT);\n}\nboolean isEditableType(ArgType t) { return EDITABLE.contains(t); }\n// call: if (!isEditableType(type)) { disableEditUi(); return; }","typeGuard":"static boolean isSupportedEditType(ArgType type) {\n    return type == ArgType.INT || type == ArgType.STRING || type == ArgType.LONG\n        || type == ArgType.FLOAT || type == ArgType.DOUBLE || type == ArgType.OBJECT;\n}","tryCatchPattern":"// UI layer: degrade gracefully instead of crashing the debug session\nRuntimeType rt;\ntry {\n    rt = castType(type);\n} catch (JadxRuntimeException e) {\n    if (e.getMessage().startsWith(\"Unexpected type\")) {\n        LOG.info(\"Edit unsupported for type {}\", type);\n        return false;\n    }\n    throw e;\n}","preventionTips":["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."],"tags":["debugger","type-conversion","argtype","registers"],"backgroundTag":null,"analyzedSha":"e738a26571d02919f01df40de93bc9a44dee4e18","analyzedAt":"2026-08-14T00:10:24.238Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}