skylot/jadx · error · JadxArgsValidateException

Unknown command: {}. Expected one of: {}

Error message

Unknown command: {}. Expected one of: {}

What it means

Thrown by FieldNodeAdapter.read after the declaring class resolved successfully but clsNode.searchFieldByShortId(sign) returns null. The persisted field short id (name + signature hash) does not match any field on the class in the current APK, so the cached reference cannot be reattached. It indicates the class exists but its fields changed since the cache was written.

Source

Thrown at jadx-cli/src/main/java/jadx/cli/JadxCLICommands.java:30

public class JadxCLICommands {
	private static final Map<String, ICommand> COMMANDS_MAP = new LinkedHashMap<>();

	static {
		JadxCLICommands.register(new CommandPlugins());
	}

	public static void register(ICommand command) {
		COMMANDS_MAP.put(command.name(), command);
	}

	public static void append(JCommander.Builder builder) {
		COMMANDS_MAP.forEach(builder::addCommand);
	}

	public static boolean process(JCommanderWrapper jcw, JCommander jc, String parsedCommand) {
		ICommand command = COMMANDS_MAP.get(parsedCommand);
		if (command == null) {
			throw new JadxArgsValidateException("Unknown command: " + parsedCommand
					+ ". Expected one of: " + COMMANDS_MAP.keySet());
		}
		JCommander subCommander = jc.getCommands().get(parsedCommand);
		command.process(jcw, subCommander);
		return true;
	}
}

View on GitHub (pinned to e738a26571)

Solutions

  1. Delete the project's jadx cache directory so annotations are recomputed from the current APK.
  2. Confirm the input APK is unchanged since the cache was generated; if it changed, treat the cache as invalid.
  3. Upgrade jadx so a field miss invalidates the entry instead of throwing.
  4. If fields are intentionally volatile, disable the on-disk annotation cache.

Example fix

// before
FieldNode fieldNode = clsNode.searchFieldByShortId(sign);
if (fieldNode == null) {
    throw new RuntimeException("Field not found: " + cls + "." + sign);
}

// after
FieldNode fieldNode = clsNode.searchFieldByShortId(sign);
if (fieldNode == null) {
    throw new CacheInvalidatedException("Field not found: " + cls + "." + sign);
}
Defensive patterns

Strategy: validation

Validate before calling

// After resolving the class, confirm the field short id still exists:
FieldNode fieldNode = clsNode.searchFieldByShortId(sign);
if (fieldNode == null) {
    invalidateCache(); // field set changed since write
    return;
}

Try / catch

try {
    return adapter.read(in);
} catch (RuntimeException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Field not found")) {
        LOG.warn("Stale cache entry, rebuilding: {}", e.getMessage());
        cacheDir.delete();
        return null;
    }
    throw e;
}

Prevention

When it happens

Trigger: Cache deserialization reaches a field whose short id no longer matches: the class was kept but a field was renamed, retyped, added or removed between the cache-write APK and the currently loaded APK. searchFieldByShortId fails and the read aborts.

Common situations: APK recompiled with a different proguard mapping; field renamed by an optimizer; jadx version change that alters short-id computation; partial cache reuse after editing the input.

Related errors


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