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.

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)

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.