skylot/jadx · error · JadxArgsValidateException

Input class can't be saved by current jadx settings (marked

Error message

Input class can't be saved by current jadx settings (marked as DONT_GENERATE)

What it means

Thrown by RuntimeType.fromJdwpTag(int tag) when a JDWP value tag from the device does not match any of the sixteen standard JDWP.Tag constants handled by the switch (ARRAY, BYTE, CHAR, OBJECT, FLOAT, DOUBLE, INT, LONG, SHORT, VOID, BOOLEAN, STRING, THREAD, THREAD_GROUP, CLASS_LOADER, CLASS_OBJECT). All well-formed JDWP tags are covered, so reaching the default means the device sent a non-standard tag value or the byte stream is misaligned/corrupt. It is a protocol-level surprise from the debugged runtime.

Source

Thrown at jadx-cli/src/main/java/jadx/cli/SingleClassMode.java:40

	public static boolean process(JadxDecompiler jadx, JadxCLIArgs cliArgs) {
		String singleClass = cliArgs.getSingleClass();
		String singleClassOutput = cliArgs.getSingleClassOutput();
		if (singleClass == null && singleClassOutput == null) {
			return false;
		}
		ClassNode clsForProcess;
		if (singleClass != null) {
			clsForProcess = jadx.getRoot().resolveClass(singleClass);
			if (clsForProcess == null) {
				clsForProcess = jadx.getRoot().getClasses().stream()
						.filter(cls -> cls.getClassInfo().getAliasFullName().equals(singleClass))
						.findFirst().orElse(null);
			}
			if (clsForProcess == null) {
				throw new JadxArgsValidateException("Input class not found: " + singleClass);
			}
			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");
			}
		}

View on GitHub (pinned to e738a26571)

Solutions

  1. Try debugging on a stock Android device/emulator image whose JDWP tags conform to the spec.
  2. Upgrade jadx and its bundled jdwp library; newer versions may handle additional tags.
  3. Verify the ID sizes negotiated in initJDWP are correct for the target (a wrong field size desynchronizes every subsequent tag read).
  4. If you control the code, make fromJdwpTag return null and have callers skip the value rather than abort the debug session.

Example fix

// before
default:
    throw new SmaliDebuggerException("Unexpected value: " + tag);

// after
default:
    LOG.warn("Unknown JDWP value tag: {}", tag);
    return null; // caller skips this value instead of killing the session
Defensive patterns

Strategy: validation

Validate before calling

// Validate the tag is one JDWP actually defines before calling fromJdwpTag:
if (!JDWP_TAG_SET.contains(tag)) {
    LOG.warn("Skipping value with non-standard JDWP tag {}", tag);
    return null;
}
RuntimeType rt = RuntimeType.fromJdwpTag(tag);

Type guard

static boolean isKnownJdwpTag(int tag) {
    return switch (tag) {
        case JDWP.Tag.ARRAY, JDWP.Tag.BYTE, JDWP.Tag.CHAR, JDWP.Tag.OBJECT,
             JDWP.Tag.FLOAT, JDWP.Tag.DOUBLE, JDWP.Tag.INT, JDWP.Tag.LONG,
             JDWP.Tag.SHORT, JDWP.Tag.VOID, JDWP.Tag.BOOLEAN, JDWP.Tag.STRING,
             JDWP.Tag.THREAD, JDWP.Tag.THREAD_GROUP, JDWP.Tag.CLASS_LOADER,
             JDWP.Tag.CLASS_OBJECT -> true;
        default -> false;
    };
}

Try / catch

RuntimeType rt;
try {
    rt = RuntimeType.fromJdwpTag(tag);
} catch (SmaliDebuggerException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Unexpected value")) {
        LOG.warn("Non-standard JDWP tag {}, skipping value", tag);
        return null;
    }
    throw e;
}

Prevention

When it happens

Trigger: Decoding a JDWP value whose type tag byte is outside the spec's defined set, e.g. a vendor-specific tag from a non-conformant ART build, or a tag read from a misaligned packet buffer so the decoder reads the wrong byte as a tag. Fires while interpreting variable values returned to the debugger.

Common situations: Debugging on an unusual device/ROM whose ART emits tag values not in the standard set; a JDWP packet decode that got out of sync (wrong ID sizes from initJDWP); emulator with a non-standard runtime; reading a value whose encoding the current jdwp library does not model.

Related errors


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