NationalSecurityAgency/ghidra · error · IllegalArgumentException

Cannot apply to both parameters of

Error message

Cannot apply {} to both parameters of {}

What it means

Thrown by AutoOptionsListener.MethodOptionSetter when a two-parameter @AutoOptionConsumed method annotates BOTH parameters with @NewValue. The framework maps old/new values to parameters by annotation position; applying @NewValue to both leaves no slot for the old value and is ambiguous, so it is rejected. The message names the annotation class and method.

Solutions

  1. Annotate at most one parameter with @NewValue; mark the other with @OldValue or leave it unannotated.
  2. Use a single-parameter method if only the new value is needed.
  3. Review the two-parameter contract: one slot is the new value, the other the old value.

Example fix

// before
@AutoOptionConsumed("my.opt")
void onChange(@NewValue int a, @NewValue int b) { ... } // both @NewValue

// after
@AutoOptionConsumed("my.opt")
void onChange(@OldValue int oldVal, @NewValue int newVal) { ... }
Defensive patterns

Strategy: validation

Validate before calling

long newCount = Arrays.stream(method.getParameters())
    .filter(p -> p.getAnnotation(NewValue.class) != null).count();
if (newCount > 1) {
  throw new IllegalStateException("Only one @NewValue per method: " + method);
}

Prevention

When it happens

Trigger: Annotating a method like void changed(@NewValue String a, @NewValue String b) — both params marked as the new value. Copy-paste annotation errors where both parameters receive the same annotation.

Common situations: Misunderstanding the @NewValue/@OldValue parameter-mapping contract; editing a method signature and re-annotating both params identically; IDE auto-annotation applying to all params.

Related errors


AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14). Data as JSON: /api/errors/760d0dfe99bfe755. Report an issue: GitHub.

Appendix: source

Thrown at Ghidra/Debug/ProposedUtils/src/main/java/ghidra/framework/options/AutoOptionsListener.java:135

			method.setAccessible(true);

			Parameter[] parameters = method.getParameters();
			if (parameters.length == 0) {
				this.order = ParamOrder.NONE;
			}
			else if (parameters.length == 1) {
				if (parameters[0].getAnnotation(OldValue.class) != null) {
					this.order = ParamOrder.OLD_ONLY;
				}
				else {
					this.order = ParamOrder.NEW_ONLY;
				}
			}
			else if (parameters.length == 2) {
				if (parameters[0].getAnnotation(NewValue.class) != null) {
					if (parameters[1].getAnnotation(NewValue.class) != null) {
						throw new IllegalArgumentException("Cannot apply " +
							NewValue.class.getName() + " to both parameters of " + method);
					}
					this.order = ParamOrder.NEW_OLD;
				}
				else if (parameters[0].getAnnotation(OldValue.class) != null) {
					if (parameters[1].getAnnotation(OldValue.class) != null) {
						throw new IllegalArgumentException("Cannot apply " +
							OldValue.class.getName() + " to both parameters of " + method);
					}
					this.order = ParamOrder.OLD_NEW;
				}
				else {
					if (parameters[1].getAnnotation(NewValue.class) != null) {
						this.order = ParamOrder.OLD_NEW;
					}
					else {
						this.order = ParamOrder.NEW_OLD;
					}

View on GitHub (pinned to d5f144c24d)