spring-projects/spring-framework · error · IllegalStateException

No primary or single unique constructor found for {clazz}

Error message

No primary or single unique constructor found for {clazz}

What it means

Thrown as IllegalStateException (not a BeansException) by getResolvableConstructor at BeanUtils.java:265 when a class has multiple public constructors, no Kotlin primary constructor, is not a record, and has no no-arg default constructor either — i.e. there is no single unambiguous constructor Spring can pick. The algorithm in getResolvableConstructor exhausted primary-constructor, single-public, single-non-public, and default-constructor lookup.

Source

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

		else if (ctors.length == 0) {
			// No public constructors -> check non-public
			ctors = clazz.getDeclaredConstructors();
			if (ctors.length == 1) {
				// A single non-public constructor, for example, from a non-public record type
				return (Constructor<T>) ctors[0];
			}
		}

		// Several constructors -> let's try to take the default constructor
		try {
			return clazz.getDeclaredConstructor();
		}
		catch (NoSuchMethodException ex) {
			// Giving up...
		}

		// No unique constructor at all
		throw new IllegalStateException("No primary or single unique constructor found for " + clazz);
	}

	/**
	 * Return the primary constructor of the provided class. For Kotlin classes, this
	 * returns the Java constructor corresponding to the Kotlin primary constructor
	 * (as defined in the Kotlin specification). For Java records, this returns the
	 * canonical constructor. Otherwise, this simply returns {@code null}.
	 * @param clazz the class to check
	 * @since 5.0
	 * @see <a href="https://kotlinlang.org/docs/reference/classes.html#constructors">Kotlin constructors</a>
	 * @see <a href="https://docs.oracle.com/javase/specs/jls/se17/html/jls-8.html#jls-8.10.4">Record constructor declarations</a>
	 */
	public static <T> @Nullable Constructor<T> findPrimaryConstructor(Class<T> clazz) {
		Assert.notNull(clazz, "Class must not be null");
		if (KOTLIN_REFLECT_PRESENT && KotlinDetector.isKotlinType(clazz)) {
			return KotlinDelegate.findPrimaryConstructor(clazz);
		}
		if (clazz.isRecord()) {

View on GitHub (pinned to 69bf83ad71)

Solutions

  1. Annotate the intended constructor with @ConstructorProperties or make it the only public constructor.
  2. Add a no-arg default constructor so the fallback in getResolvableConstructor succeeds.
  3. Reduce to a single public constructor (make others non-public).
  4. Use an explicit @Bean factory method or explicit Constructor argument instead of relying on auto-resolution.

Example fix

// before: ambiguous
public class Props {
    public Props(String a) {}
    public Props(String a, int b) {}
}

// after
public class Props {
    public Props() {}
    public Props(String a, int b) { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

Constructor<?> c = BeanUtils.findPrimaryConstructor(clazz);
if (c == null) {
    Constructor<?>[] publics = clazz.getConstructors();
    if (publics.length != 1) {
        // expect multiple -> ensure a no-arg ctor exists or annotate one
        try { clazz.getDeclaredConstructor(); }
        catch (NoSuchMethodException e) { /* will fail */ }
    }
}

Type guard

static boolean hasResolvableConstructor(Class<?> c) {
    if (BeanUtils.findPrimaryConstructor(c) != null) return true;
    if (c.getConstructors().length == 1) return true;
    try { c.getDeclaredConstructor(); return true; }
    catch (NoSuchMethodException e) { return false; }
}

Try / catch

try {
    BeanUtils.getResolvableConstructor(clazz);
} catch (IllegalStateException ex) {
    // ambiguous -> supply explicit constructor to the binder/factory
    Constructor<?> chosen = clazz.getDeclaredConstructor(String.class, int.class);
}

Prevention

When it happens

Trigger: Calling getResolvableConstructor(clazz) (used by @ConfigurationProperties, constructor binding, data binding) on a class with two or more public constructors and no no-arg constructor. None of the disambiguation heuristics (single public, single non-public, default) apply, so resolution fails.

Common situations: @ConfigurationProperties class with overloaded constructors; constructor-binding DTOs with multiple explicit constructors; domain entities with several public constructors and no default; Spring Boot 3 / Spring 6 stricter constructor binding that requires an unambiguous constructor.

Related errors


AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09). Data as JSON: /api/errors/51e59ee675b2aa1a. Report an issue: GitHub.