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
- On the client: disable submit while in flight, and retry only after the previous request definitively failed.
- If business semantics allow, set idempotent.timeout() short enough to cover only the processing window.
- Set deleteKeyWhenException=true (if not default) so failed calls can be retried immediately.
- 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
- Disable submit controls during in-flight requests
- Enable deleteKeyWhenException so failed requests are retryable
- Never auto-retry on code 900 — it means a duplicate, not a failure
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
- 423
- LoginUser(%d) Table(%s/%s) 未返回数据权限
- AreaUtils 初始化失败
- IPUtils 初始化失败
- TenantContextHolder 不存在租户编号!可参考文档:https://doc.iocoder.cn
AI-assisted analysis of YunaiV/yudao-cloud@477be9dd49 (2026-08-14).
Data as JSON: /api/errors/2a768ade8c980a32.
Report an issue: GitHub.