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

  1. Check for an existing client with findByClientId(clientId) before calling addClientDetails, and call updateClientDetails instead if it exists.
  2. Use a unique clientId for each new registration.
  3. If re-seeding, delete the existing client first via removeClientDetails(clientId).
  4. 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

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.