xkcoding/spring-boot-demo · warning · ClientAlreadyExistsException
Client id %s already exist.
Error message
Client id %s already exist.
What it means
Thrown by addClientDetails when a SysClientDetails with the same clientId already exists in the repository. This is Spring Security OAuth2's ClientAlreadyExistsException, part of the ClientDetailsService contract. The check queries findFirstByClientId before inserting; if present, registration is rejected.
Source
Thrown at demo-oauth/oauth-authorization-server/src/main/java/com/xkcoding/oauth/service/impl/SysClientDetailsServiceImpl.java:43
private final SysClientDetailsRepository sysClientDetailsRepository;
private final PasswordEncoder passwordEncoder;
@Override
public ClientDetails loadClientByClientId(String id) throws ClientRegistrationException {
return sysClientDetailsRepository.findFirstByClientId(id).orElseThrow(() -> new ClientRegistrationException("Loading client exception."));
}
@Override
public SysClientDetails findByClientId(String clientId) {
return sysClientDetailsRepository.findFirstByClientId(clientId).orElseThrow(() -> new ClientRegistrationException("Loading client exception."));
}
@Override
public void addClientDetails(SysClientDetails clientDetails) throws ClientAlreadyExistsException {
clientDetails.setId(null);
if (sysClientDetailsRepository.findFirstByClientId(clientDetails.getClientId()).isPresent()) {
throw new ClientAlreadyExistsException(String.format("Client id %s already exist.", clientDetails.getClientId()));
}
sysClientDetailsRepository.save(clientDetails);
}
@Override
public void updateClientDetails(SysClientDetails clientDetails) throws NoSuchClientException {
SysClientDetails exist = sysClientDetailsRepository.findFirstByClientId(clientDetails.getClientId()).orElseThrow(() -> new NoSuchClientException("No such client!"));
clientDetails.setClientSecret(exist.getClientSecret());
sysClientDetailsRepository.save(clientDetails);
}
@Override
public void updateClientSecret(String clientId, String clientSecret) throws NoSuchClientException {
SysClientDetails exist = sysClientDetailsRepository.findFirstByClientId(clientId).orElseThrow(() -> new NoSuchClientException("No such client!"));
exist.setClientSecret(passwordEncoder.encode(clientSecret));
sysClientDetailsRepository.save(exist);
}
View on GitHub (pinned to 87a142f960)
Solutions
- Check for an existing client with findByClientId(clientId) before calling addClientDetails, and call updateClientDetails instead if it exists.
- Use a unique clientId for each new registration.
- If re-seeding, delete the existing client first via removeClientDetails(clientId).
- Handle ClientAlreadyExistsException in the calling layer and inform the user the clientId is taken.
Example fix
// before — blindly calls addClientDetails
sysClientDetailsService.addClientDetails(clientDetails);
// after — check first, then add or update
if (sysClientDetailsService.findByClientId(clientDetails.getClientId()) != null) {
sysClientDetailsService.updateClientDetails(clientDetails);
} else {
sysClientDetailsService.addClientDetails(clientDetails);
} Defensive patterns
Strategy: validation
Validate before calling
// Check for existing client before adding
SysClientDetails existing = sysClientDetailsRepository
.findFirstByClientId(clientDetails.getClientId()).orElse(null);
if (existing != null) {
// Update instead of add, or inform the caller
throw new IllegalArgumentException("ClientId already taken: " + clientDetails.getClientId());
} Try / catch
try {
sysClientDetailsService.addClientDetails(clientDetails);
} catch (ClientAlreadyExistsException e) {
log.warn("Client {} already exists", clientDetails.getClientId());
// Optionally update instead
sysClientDetailsService.updateClientDetails(clientDetails);
} Prevention
- Always check findByClientId before addClientDetails to avoid the exception path.
- Generate clientIds with a uniqueness guarantee (UUID or a sequence).
- Handle ClientAlreadyExistsException gracefully in the calling layer.
When it happens
Trigger: Calling addClientDetails(clientDetails) where clientDetails.getClientId() matches an existing row in the sys_client_details table. This occurs during OAuth2 client registration setup or when re-running a seed script.
Common situations: Re-running a database seed or migration script that inserts OAuth2 clients; two modules or services registering the same clientId; manual client setup via admin UI hitting a duplicate; CI/CD pipeline re-deploying with seed data that already ran.
Related errors
AI-assisted analysis of xkcoding/spring-boot-demo@87a142f960 (2026-08-14).
Data as JSON: /api/errors/bfd874bffd10fd9b.
Report an issue: GitHub.