spring-projects/spring-framework · error · IllegalArgumentException

Cannot resolve method '${methodName}' to a unique method. At

Error message

Cannot resolve method '${methodName}' to a unique method. Attempted to resolve to overloaded method with the least number of parameters but there were ${numMethodsFoundWithCurrentMinimumArgs} candidates.

What it means

Thrown as IllegalArgumentException by findMethodWithMinimalParameters / resolveSignature when more than one method with the given name share the smallest parameter count, so the 'least parameters' heuristic cannot pick a unique winner (BeanUtils.java:425-430). The count of ambiguous candidates is included in the message.

Source

Thrown at spring-beans/src/main/java/org/springframework/beans/BeanUtils.java:426

				int numParams = method.getParameterCount();
				if (targetMethod == null || numParams < targetMethod.getParameterCount()) {
					targetMethod = method;
					numMethodsFoundWithCurrentMinimumArgs = 1;
				}
				else if (!method.isBridge() && targetMethod.getParameterCount() == numParams) {
					if (targetMethod.isBridge()) {
						// Prefer regular method over bridge...
						targetMethod = method;
					}
					else {
						// Additional candidate with same length
						numMethodsFoundWithCurrentMinimumArgs++;
					}
				}
			}
		}
		if (numMethodsFoundWithCurrentMinimumArgs > 1) {
			throw new IllegalArgumentException("Cannot resolve method '" + methodName +
					"' to a unique method. Attempted to resolve to overloaded method with " +
					"the least number of parameters but there were " +
					numMethodsFoundWithCurrentMinimumArgs + " candidates.");
		}
		return targetMethod;
	}

	/**
	 * Parse a method signature in the form {@code methodName[([arg_list])]},
	 * where {@code arg_list} is an optional, comma-separated list of fully-qualified
	 * type names, and attempts to resolve that signature against the supplied {@code Class}.
	 * <p>When not supplying an argument list ({@code methodName}) the method whose name
	 * matches and has the least number of parameters will be returned. When supplying an
	 * argument type list, only the method whose name and argument types match will be returned.
	 * <p>Note then that {@code methodName} and {@code methodName()} are <strong>not</strong>
	 * resolved in the same way. The signature {@code methodName} means the method called
	 * {@code methodName} with the least number of arguments, whereas {@code methodName()}
	 * means the method called {@code methodName} with exactly 0 arguments.

View on GitHub (pinned to e8729d0438)

Solutions

  1. Provide the full signature with argument types in resolveSignature: 'methodName(int, java.lang.String)' to disambiguate.
  2. Rename one of the colliding overloads if you control the class.
  3. Use findMethod(clazz, name, paramTypes...) directly with explicit parameter types instead of the minimal-args heuristic.
  4. Reduce accidental same-arity overloads (e.g. consolidate or remove one).

Example fix

// before
resolveSignature("persist", repo.getClass()); // persist(int) & persist(String) ambiguous

// after
resolveSignature("persist(int)", repo.getClass());
Defensive patterns

Strategy: validation

Validate before calling

// Detect same-arity overload collision before resolving
long minArity = Arrays.stream(clazz.getMethods())
    .filter(m -> m.getName().equals(methodName) && !m.isBridge())
    .mapToInt(Method::getParameterCount).min().orElse(-1);
long count = Arrays.stream(clazz.getMethods())
    .filter(m -> m.getName().equals(methodName) && !m.isBridge()
        && m.getParameterCount() == minArity).count();
if (count > 1) { /* supply full signature instead */ }

Type guard

public static boolean methodResolvableByName(Class<?> c, String name) {
  try { BeanUtils.findMethodWithMinimalParameters(c, name); return true; }
  catch (IllegalArgumentException e) { return false; }
}

Try / catch

try { BeanUtils.resolveSignature(name, clazz); }
catch (IllegalArgumentException e) {
  BeanUtils.resolveSignature(name + "(int)", clazz); // disambiguate with arg types
}

Prevention

When it happens

Trigger: resolveSignature('methodName', clazz) with no argument list, or findMethodWithMinimalParameters(clazz, 'methodName'), where the class declares two or more overloads with the same minimal arity (e.g. foo() and foo() from different interfaces, or foo(int) and foo(String)).

Common situations: Method-resolver utilities, scheduler/transaction attribute parsing, pointcut expressions, and AOP config that take a bare method name. Class hierarchies introducing same-arity overloads. Bridge methods (note the code excludes isBridge from counting, but real overloads still collide).

Related errors


AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04). Data as JSON: /data/errors/01c80a1507cd4972.json. Report an issue: GitHub.