spring-projects/spring-security · error · DataRetrievalFailureException

The ClientRegistration with id '${clientRegistrationId}' exi

Error message

The ClientRegistration with id '${clientRegistrationId}' exists in the data source, however, it was not found in the ClientRegistrationRepository.

What it means

JdbcOAuth2AuthorizedClientService.OAuth2AuthorizedClientRowMapper loads an OAuth2AuthorizedClient row from the database and resolves the ClientRegistration via the configured ClientRegistrationRepository. When the repository cannot find a registration matching the row's client_registration_id, it throws this DataRetrievalFailureException, because an authorized client row without a corresponding in-memory registration cannot be reconstructed.

Source

Thrown at oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/JdbcOAuth2AuthorizedClientService.java:262

		protected LobHandler lobHandler = new DefaultLobHandler();

		public OAuth2AuthorizedClientRowMapper(ClientRegistrationRepository clientRegistrationRepository) {
			Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
			this.clientRegistrationRepository = clientRegistrationRepository;
		}

		public final void setLobHandler(LobHandler lobHandler) {
			Assert.notNull(lobHandler, "lobHandler cannot be null");
			this.lobHandler = lobHandler;
		}

		@Override
		public OAuth2AuthorizedClient mapRow(ResultSet rs, int rowNum) throws SQLException {
			String clientRegistrationId = rs.getString("client_registration_id");
			ClientRegistration clientRegistration = this.clientRegistrationRepository
				.findByRegistrationId(clientRegistrationId);
			if (clientRegistration == null) {
				throw new DataRetrievalFailureException(
						"The ClientRegistration with id '" + clientRegistrationId + "' exists in the data source, "
								+ "however, it was not found in the ClientRegistrationRepository.");
			}
			OAuth2AccessToken.TokenType tokenType = null;
			if (OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(rs.getString("access_token_type"))) {
				tokenType = OAuth2AccessToken.TokenType.BEARER;
			}
			OAuth2AccessToken.TokenType tokenTypeToUse = (tokenType != null) ? tokenType
					: OAuth2AccessToken.TokenType.BEARER;
			String tokenValue = new String(this.lobHandler.getBlobAsBytes(rs, "access_token_value"),
					StandardCharsets.UTF_8);
			Timestamp issuedAtTs = rs.getTimestamp("access_token_issued_at");
			Timestamp expiresAtTs = rs.getTimestamp("access_token_expires_at");
			Instant issuedAt = (issuedAtTs != null) ? issuedAtTs.toInstant() : null;
			Instant expiresAt = (expiresAtTs != null) ? expiresAtTs.toInstant() : null;
			Set<String> scopes = Collections.emptySet();
			String accessTokenScopes = rs.getString("access_token_scopes");
			if (accessTokenScopes != null) {

View on GitHub (pinned to 96852e8860)

Solutions

  1. Delete or clean stale rows in oauth2_authorized_client whose client_registration_id no longer exists in the repository.
  2. Restore the missing registration in your ClientRegistrationRepository / application properties so the id matches the DB rows.
  3. If registrations are dynamic, use a ClientRegistrationRepository that can resolve all persisted registration ids (e.g. a Jdbc-backed or issuer-based repository).
  4. Use a custom RowMapper or wrap the service to skip/log rows whose registration is absent instead of failing the whole load.

Example fix

// before: stale DB rows for removed client 'old-client'
spring.security.oauth2.client.registration.old-client.client-id=...
// after: either re-add the registration or purge rows
DELETE FROM oauth2_authorized_client WHERE client_registration_id = 'old-client';
Defensive patterns

Strategy: try-catch

Validate before calling

boolean registered = clientRegistrationRepository.findByRegistrationId(rowId) != null;
if (!registered) { /* skip/purge row or re-add registration */ }

Type guard

boolean hasRegistration(String id) {
    return clientRegistrationRepository.findByRegistrationId(id) != null;
}

Try / catch

try {
    OAuth2AuthorizedClient client = service.loadAuthorizedClient(registrationId, principalName);
} catch (DataRetrievalFailureException ex) {
    // purge stale row or fall back to a fresh authorization flow
    log.warn("orphaned authorized client row: {}", ex.getMessage());
}

Prevention

When it happens

Trigger: Calling loadAuthorizedClient/removeAuthorizedClient/updateAuthorizedClient on JdbcOAuth2AuthorizedClientService when the oauth2_authorized_client table contains a row whose client_registration_id is not registered in the ClientRegistrationRepository (which is typically an InMemoryClientRegistrationRepository built from static spring.security.oauth2.client.registration.* properties).

Common situations: Client registration removed or renamed in application.yml while old authorized-client rows persist in the database; multi-tenant setups where rows are written for registrations that are not loaded on that instance; switching from a dynamic/OIDC-discovery repository to a static one; running multiple apps or profiles against a shared database.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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