apache/shenyu · error · ShenyuException

unique index conflict, please enter again

Error message

unique index conflict, please enter again

What it means

AiProxyApiKeyServiceImpl.create() enforces uniqueness of proxyApiKey within a selector: if the client supplied a key that mapper.proxyApiKeyExisted() already finds for this selector, ShenyuResultMessage.UNIQUE_INDEX_CONFLICT_ERROR is thrown. Note the key is auto-generated only when blank, so an explicitly supplied duplicate is the only way to hit this path.

Solutions

  1. Omit proxyApiKey in the create payload so the server auto-generates a unique key via SignUtils.generateKey().
  2. Supply a different, unused key value.
  3. Query existing keys for the selector first and either update the existing record instead of creating a new one.
  4. Client-side: treat ShenyuResultMessage.UNIQUE_INDEX_CONFLICT_ERROR as 'record already exists' and fall back to an update call.

Example fix

// before
POST /ai-proxy-key {"proxyApiKey":"sk-existing","namespaceId":"ns1"} // selector sel1, duplicate key
// after
POST /ai-proxy-key {"namespaceId":"ns1"} // proxyApiKey omitted, server generates unique key
Defensive patterns

Strategy: try-catch

Validate before calling

// check uniqueness before creating
if (StringUtils.isNotBlank(dto.getProxyApiKey())
        && apiKeyMapper.proxyApiKeyExisted(selectorId, dto.getProxyApiKey())) {
    // record already exists — update instead of create
}

Try / catch

try {
    apiKeyService.createOrUpdate(dto, selectorId);
} catch (ShenyuException e) {
    if (ShenyuResultMessage.UNIQUE_INDEX_CONFLICT_ERROR.equals(e.getMessage())) {
        // fall back to update of the existing key for this selector
    }
    throw e;
}

Prevention

When it happens

Trigger: Creating a second API key for the same selector with proxyApiKey set to a key that already exists on that selector; replaying a create request with the same explicit key after the first succeeded; importing/exporting config containing duplicate keys.

Common situations: Operators manually re-entering a key they copied earlier; config restore scripts re-importing keys idempotently without checking existence; concurrent create calls racing between the uniqueness check and insert.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12). Data as JSON: /api/errors/e29b6809e8ae13ef. Report an issue: GitHub.

Appendix: source

Thrown at shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/AiProxyApiKeyServiceImpl.java:98

        if (StringUtils.isBlank(entity.getId())) {
            entity.setId(UUIDUtils.getInstance().generateShortUuid());
        }
        if (StringUtils.isBlank(entity.getProxyApiKey())) {
            entity.setProxyApiKey(SignUtils.generateKey());
        }
        // validate namespace
        if (StringUtils.isBlank(entity.getNamespaceId())) {
            throw new ShenyuException(ShenyuResultMessage.PARAMETER_ERROR);
        }
        // validate selector id
        if (StringUtils.isBlank(selectorId)) {
            throw new ShenyuException(ShenyuResultMessage.PARAMETER_ERROR);
        }
        entity.setSelectorId(selectorId);
        // unique check for proxyApiKey if provided
        if (StringUtils.isNotBlank(entity.getProxyApiKey())
                && Boolean.TRUE.equals(mapper.proxyApiKeyExisted(selectorId, entity.getProxyApiKey()))) {
            throw new ShenyuException(ShenyuResultMessage.UNIQUE_INDEX_CONFLICT_ERROR);
        }
        if (Objects.isNull(entity.getEnabled())) {
            entity.setEnabled(Boolean.TRUE);
        }
        // back fill generated fields to response dto first
        dto.setId(entity.getId());
        dto.setProxyApiKey(entity.getProxyApiKey());
        dto.setEnabled(entity.getEnabled());
        final int rows = mapper.insert(entity);
        publishChange(DataEventTypeEnum.CREATE, entity);
        return rows;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int update(final ProxyApiKeyDTO dto) {
        final ProxyApiKeyDO entity = ProxyApiKeyTransfer.INSTANCE.mapToEntity(dto);
        // id is required for update

View on GitHub (pinned to 567142e072)