YunaiV/yudao-cloud · warning · ServiceException

900

900

Error message

idempotent.message()

What it means

IdempotentAspect implements deduplication: before the method runs it setIfAbsent's a Redis key derived from the user/keyResolver. If the key already exists (a duplicate request inside the timeout window), it throws ServiceException with code 900 (REPEATED_REQUESTS) and the annotation's message (default 'idempotent.message()' literal shown when no custom message set). If the method throws, the key is optionally deleted so a genuine retry can succeed.

Source

Thrown at yudao-framework/yudao-spring-boot-starter-protection/src/main/java/cn/iocoder/yudao/framework/idempotent/core/aop/IdempotentAspect.java:52

    public IdempotentAspect(List<IdempotentKeyResolver> keyResolvers, IdempotentRedisDAO idempotentRedisDAO) {
        this.keyResolvers = CollectionUtils.convertMap(keyResolvers, IdempotentKeyResolver::getClass);
        this.idempotentRedisDAO = idempotentRedisDAO;
    }

    @Around(value = "@annotation(idempotent)")
    public Object aroundPointCut(ProceedingJoinPoint joinPoint, Idempotent idempotent) throws Throwable {
        // 获得 IdempotentKeyResolver
        IdempotentKeyResolver keyResolver = keyResolvers.get(idempotent.keyResolver());
        Assert.notNull(keyResolver, "找不到对应的 IdempotentKeyResolver");
        // 解析 Key
        String key = keyResolver.resolver(joinPoint, idempotent);

        // 1. 锁定 Key
        boolean success = idempotentRedisDAO.setIfAbsent(key, idempotent.timeout(), idempotent.timeUnit());
        // 锁定失败,抛出异常
        if (!success) {
            log.info("[aroundPointCut][方法({}) 参数({}) 存在重复请求]", joinPoint.getSignature().toString(), joinPoint.getArgs());
            throw new ServiceException(GlobalErrorCodeConstants.REPEATED_REQUESTS.getCode(), idempotent.message());
        }

        // 2. 执行逻辑
        try {
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            // 3. 异常时,删除 Key
            // 参考美团 GTIS 思路:https://tech.meituan.com/2016/09/29/distributed-system-mutually-exclusive-idempotence-cerberus-gtis.html
            if (idempotent.deleteKeyWhenException()) {
                idempotentRedisDAO.delete(key);
            }
            throw throwable;
        }
    }

}

View on GitHub (pinned to 477be9dd49)

Solutions

  1. On the client: disable submit while in flight, and retry only after the previous request definitively failed.
  2. If business semantics allow, set idempotent.timeout() short enough to cover only the processing window.
  3. Set deleteKeyWhenException=true (if not default) so failed calls can be retried immediately.
  4. On the server, catch ServiceException and map code 900 to a friendly '请求正在处理,请勿重复提交' response instead of a 500.

Example fix

@Before("...") // client-side guard
function submit() {
  if (submitting) return;
  submitting = true;
  try { await api.saveOrder(form); }
  finally { submitting = false; }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side: guard against double submit
if (inFlight) return;
inFlight = true;
try { /* call */ } finally { inFlight = false; }

Try / catch

try {
    result = api.submit(form);
} catch (ServiceException e) {
    if (GlobalErrorCodeConstants.REPEATED_REQUESTS.getCode().equals(e.getCode())) {
        return friendly("请求正在处理,请勿重复提交"); // no retry
    }
    throw e;
}

Prevention

When it happens

Trigger: The same user hits an @Idempotent endpoint twice within idempotent.timeout() (default window) — double-click submit, frontend retry, or concurrent duplicate requests. Key is typically user+method+params (UserIdempotentKeyResolver) or md5 of args (ExprIdempotentKeyResolver).

Common situations: Form double-submission without disabling the button; HTTP client retrying on timeout while the first request is still processing; mobile clients on flaky networks auto-retrying; batch scripts re-invoking an API.

Related errors


AI-assisted analysis of YunaiV/yudao-cloud@477be9dd49 (2026-08-14). Data as JSON: /api/errors/2a768ade8c980a32. Report an issue: GitHub.