spring-projects/spring-security · error · IllegalStateException

Duplicate key ${registrationId}

Error message

Duplicate key ${registrationId}

What it means

InMemoryReactiveClientRegistrationRepository stores ClientRegistrations in an unmodifiable map keyed by registrationId. During construction it detects two registrations with the same registrationId and throws this IllegalStateException, because a duplicate key would silently overwrite a registration and break request matching.

Source

Thrown at oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/InMemoryReactiveClientRegistrationRepository.java:88

		return Mono.justOrEmpty(this.clientIdToClientRegistration.get(registrationId));
	}

	/**
	 * Returns an {@code Iterator} of {@link ClientRegistration}.
	 * @return an {@code Iterator<ClientRegistration>}
	 */
	@Override
	public Iterator<ClientRegistration> iterator() {
		return this.clientIdToClientRegistration.values().iterator();
	}

	private static Map<String, ClientRegistration> toUnmodifiableConcurrentMap(List<ClientRegistration> registrations) {
		Assert.notEmpty(registrations, "registrations cannot be null or empty");
		ConcurrentHashMap<String, ClientRegistration> result = new ConcurrentHashMap<>();
		for (ClientRegistration registration : registrations) {
			Assert.notNull(registration, "no registration can be null");
			if (result.containsKey(registration.getRegistrationId())) {
				throw new IllegalStateException(String.format("Duplicate key %s", registration.getRegistrationId()));
			}
			result.put(registration.getRegistrationId(), registration);
		}
		return Collections.unmodifiableMap(result);
	}

}

View on GitHub (pinned to 96852e8860)

Solutions

  1. Make every registrationId unique across all registrations passed to the constructor.
  2. If a bean-based config auto-generates registrations, exclude the duplicates instead of adding manual copies.
  3. Centralize registration creation (e.g. a @Bean returning the repository) so ids can't collide between config files and code.
  4. On failure, log each registration's getRegistrationId() before constructing to spot the duplicate quickly.

Example fix

// before
new InMemoryReactiveClientRegistrationRepository(List.of(
  ClientRegistration.withRegistrationId("idp")...build(),
  ClientRegistration.withRegistrationId("idp")...build())) // duplicate
// after
new InMemoryReactiveClientRegistrationRepository(List.of(
  ClientRegistration.withRegistrationId("idp")...build(),
  ClientRegistration.withRegistrationId("idp-2")...build()))
Defensive patterns

Strategy: validation

Validate before calling

Set<String> seen = new HashSet<>();
for (ClientRegistration r : registrations) {
    if (!seen.add(r.getRegistrationId())) throw new IllegalStateException("duplicate registrationId " + r.getRegistrationId());
}

Try / catch

try { new InMemoryReactiveClientRegistrationRepository(registrations); } catch (IllegalStateException e) { if (e.getMessage().startsWith("Duplicate key")) { /* dedupe list and retry */ } throw e; }

Prevention

When it happens

Trigger: new InMemoryReactiveClientRegistrationRepository(List<ClientRegistration>) where two registrations in the list share the same getRegistrationId() (checked in toUnmodifiableConcurrentMap).

Common situations: Loading registrations from application.yml via ClientRegistrationRepository beans and also declaring them manually; iterating over multiple issuer configs that all use the same registrationId placeholder; copying a registration and only changing clientId, forgetting the registrationId.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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