spring-projects/spring-security · error · ApplicationContextException

Bean ' ' must be a UserDetailsService or an…

Error message

Bean '{name}' must be a UserDetailsService or an AuthenticationUserDetailsService

What it means

UserDetailsServiceFactoryBean.authenticationUserDetailsService() resolves a bean reference into a UserDetailsService wrapper for remember-me/x509 support. After the bean is fetched, it must be an AuthenticationUserDetailsService or a UserDetailsService (or CachingUserDetailsService factory product). If the referenced bean is neither, an ApplicationContextException is thrown because no UserDetails lookup strategy can be derived from it.

Solutions

  1. Make the referenced bean implement org.springframework.security.core.userdetails.UserDetailsService (loadUserByUsername) or AuthenticationUserDetailsService.
  2. Point service-ref at an existing UserDetailsService bean instead of the wrong bean.
  3. If the bean is a CachingUserDetailsService produced by a factory, ensure the factory-bean type is correctly declared so it is recognized as a UserDetailsService.

Example fix

// before
<bean id="userService" class="com.app.UserService"/>
<remember-me service-ref="userService"/>

// after
<bean id="userService" class="com.app.UserService"/>
<bean id="uds" class="com.app.CustomUserDetailsService"/>
<remember-me service-ref="uds"/>
Defensive patterns

Strategy: type-guard

Validate before calling

Object bean = ctx.getBean(refName);
if (!(bean instanceof UserDetailsService) && !(bean instanceof AuthenticationUserDetailsService)) {
    throw new IllegalArgumentException(refName + " must be a UserDetailsService");
}

Type guard

function isUds(Object b) {
  return b instanceof org.springframework.security.core.userdetails.UserDetailsService
      || b instanceof org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
}

Try / catch

try { svc = factory.authenticationUserDetailsService(refName); }
catch (ApplicationContextException e) { log.error("Bean {} is not a UserDetailsService", refName, e); }

Prevention

When it happens

Trigger: An <http> remember-me service-ref or x509 service-ref points at a bean whose type is neither UserDetailsService nor AuthenticationUserDetailsService — e.g. referencing a plain DAO, a service class, or a bean typed Object/generic.

Common situations: Typo'd bean id that resolves to the wrong bean; refactoring moved UserDetails logic into a wrapper class no longer implementing UserDetailsService; using an XML ref to a @Component that doesn't implement the interface.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/da75ef984e78bcb8. Report an issue: GitHub.

Appendix: source

Thrown at config/src/main/java/org/springframework/security/config/http/UserDetailsServiceFactoryBean.java:90

							+ " Please use a specific Id reference.");
				}
				return (AuthenticationUserDetailsService) beans.values().toArray()[0];
			}
			uds = getUserDetailsService();
		}
		else {
			Object bean = this.beanFactory.getBean(name);
			if (bean instanceof AuthenticationUserDetailsService) {
				return (AuthenticationUserDetailsService) bean;
			}
			else if (bean instanceof UserDetailsService) {
				uds = cachingUserDetailsService(name);
				if (uds == null) {
					uds = (UserDetailsService) bean;
				}
			}
			else {
				throw new ApplicationContextException(
						"Bean '" + name + "' must be a UserDetailsService or an" + " AuthenticationUserDetailsService");
			}
		}
		return new UserDetailsByNameServiceWrapper(uds);
	}

	/**
	 * Obtains a user details service for use in RememberMeServices etc. Will return a
	 * caching version if available so should not be used for beans which need to separate
	 * the two.
	 */
	private UserDetailsService getUserDetailsService() {
		Map<String, ?> beans = getBeansOfType(CachingUserDetailsService.class);
		if (beans.isEmpty()) {
			beans = getBeansOfType(UserDetailsService.class);
		}
		if (beans.isEmpty()) {
			throw new ApplicationContextException("No UserDetailsService registered.");

View on GitHub (pinned to 96852e8860)