spring-projects/spring-framework · error · NoUniqueBeanDefinitionException

No qualifying bean of type '{}' available: expected single m

Error message

No qualifying bean of type '{}' available: expected single matching bean but found {}: {}

What it means

Thrown as NoUniqueBeanDefinitionException from qualifiedBeanOfType(ListableBeanFactory, Class, String) when two or more beans of the requested type both declare the same qualifier value. The message lists the number of matches and their bean names. This means qualifier-based disambiguation failed because the qualifier was not unique among candidates.

Source

Thrown at spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java:125

					"BeanFactory does not implement ConfigurableListableBeanFactory.)");
		}
	}

	/**
	 * Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a qualifier
	 * (for example, {@code <qualifier>} or {@code @Qualifier}) matching the given qualifier).
	 * @param beanFactory the factory to get the target bean from
	 * @param beanType the type of bean to retrieve
	 * @param qualifier the qualifier for selecting between multiple bean matches
	 * @return the matching bean of type {@code T} (never {@code null})
	 */
	private static <T> T qualifiedBeanOfType(ListableBeanFactory beanFactory, Class<T> beanType, String qualifier) {
		String[] candidateBeans = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, beanType);
		String matchingBean = null;
		for (String beanName : candidateBeans) {
			if (isQualifierMatch(qualifier::equals, beanName, beanFactory)) {
				if (matchingBean != null) {
					throw new NoUniqueBeanDefinitionException(beanType, matchingBean, beanName);
				}
				matchingBean = beanName;
			}
		}
		if (matchingBean != null) {
			return beanFactory.getBean(matchingBean, beanType);
		}
		else if (beanFactory.containsBean(qualifier) && beanFactory.isTypeMatch(qualifier, beanType)) {
			// Fallback: target bean at least found by bean name - probably a manually registered singleton.
			return beanFactory.getBean(qualifier, beanType);
		}
		else {
			throw new NoSuchBeanDefinitionException(qualifier, "No matching " + beanType.getSimpleName() +
					" bean found for qualifier '" + qualifier + "' - neither qualifier match nor bean name match!");
		}
	}

	/**

View on GitHub (pinned to 69bf83ad71)

Solutions

  1. Make qualifier values unique across beans of the same type — each candidate should have a distinct @Qualifier value.
  2. If both beans are genuinely needed, use @Primary on one to make it the default without qualifiers.
  3. Consolidate duplicate bean definitions into a single bean.

Example fix

// before
@Component
@Qualifier("cache")
public class RedisCache implements Cache { }

@Component
@Qualifier("cache")
public class MemcachedCache implements Cache { }

// after
@Component
@Qualifier("redis")
public class RedisCache implements Cache { }

@Component
@Qualifier("memcached")
public class MemcachedCache implements Cache { }
Defensive patterns

Strategy: validation

Validate before calling

// Before lookup, verify the qualifier uniquely identifies one bean
String[] candidates = ((ListableBeanFactory) beanFactory)
    .getBeanNamesForType(beanType);
long matches = Arrays.stream(candidates)
    .filter(name -> isQualifierMatch(name, qualifier, beanFactory))
    .count();
if (matches > 1) {
    throw new IllegalStateException("Qualifier '" + qualifier + "' matches " + matches + " beans");
}

Try / catch

try {
    return BeanFactoryAnnotationUtils.qualifiedBeanOfType(lbf, beanType, qualifier);
} catch (NoUniqueBeanDefinitionException ex) {
    Collection<String> found = ex.getBeanNamesFound();
    // disambiguate further or report the duplicate qualifier
    throw new IllegalStateException("Duplicate qualifier '" + qualifier + "' on: " + found, ex);
}

Prevention

When it happens

Trigger: Two or more beans of type T are registered and both declare @Qualifier("sameValue") (or an equivalent <qualifier value="sameValue"/> in XML). The loop at lines 122-128 finds the first match, then encounters a second and throws.

Common situations: Copy-pasting @Qualifier annotations across multiple bean classes without making values unique. XML configuration where <qualifier> elements share the same value across multiple <bean> definitions. Accidentally registering the same bean under multiple names with the same qualifier.

Related errors


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