iflytek/astron-agent · error · IllegalStateException
Timed out acquiring distributed lock, please try again later
Error message
Timed out acquiring distributed lock, please try again later
What it means
createOrGetUser uses a Redisson distributed lock (tryLock with 5s wait / 10s lease) to make the check-then-create idempotent across instances. If the lock cannot be acquired within 5 seconds it throws IllegalStateException('Timed out acquiring distributed lock, please try again later') rather than proceeding unsynchronized.
Solutions
- Retry the operation with backoff — the error message explicitly says 'try again later' and the user is usually created by the holder shortly after
- Reduce time inside the critical section (the findByUid + insert) so the lock is held briefly
- Lower contention: deduplicate concurrent requests per uid at the API layer (cache/single-flight)
- Check Redis health/latency and confirm lock waitTime (5s) vs DB operation time is a sane ratio
Example fix
// before
try {
boolean acquired = lock.tryLock(5, 10, TimeUnit.SECONDS);
if (!acquired) throw new IllegalStateException("Timed out acquiring distributed lock...");
// after
boolean acquired = lock.tryLock(5, 10, TimeUnit.SECONDS);
if (!acquired) {
// caller may safely retry: holder likely completed creation
Optional<UserInfo> existing = findByUid(userInfo.getUid());
if (existing.isPresent()) return existing.get();
throw new IllegalStateException("Timed out acquiring distributed lock, please try again later");
} Defensive patterns
Strategy: retry
Try / catch
try {
return userInfoService.createOrGetUser(userInfo);
} catch (IllegalStateException e) {
if (e.getMessage().contains("Timed out acquiring distributed lock")) {
Thread.sleep(200);
return userInfoService.createOrGetUser(userInfo); // single retry
}
throw e;
} Prevention
- Retry once after a short delay — the winner usually completes creation
- Keep the locked critical section fast (indexed findByUid, quick insert)
- Single-flight duplicate concurrent requests for the same uid
- Monitor Redis latency; tune tryLock waitTime to exceed worst-case section time
When it happens
Trigger: Many concurrent createOrGetUser calls for the same or contended lock keys; a lock holder stuck longer than 5s (slow DB inside the critical section); Redisson lease expiry/rebalance issues; Redis latency or connectivity problems making tryLock slow.
Common situations: Burst of first-login requests for the same new user; DB slow queries inside the locked section pushing hold time near the 10s lease; Redis network hiccups in the cluster; thread dumps showing many threads parked on tryLock.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Distributed lock acquisition timeout, please try again later
- ACQUIRE_TIMEOUT
- RELEASE_FAILED
- REDIS_CONNECTION_ERROR
- Timeout must be between 1-300 seconds
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/77bc23c46adf435b.
Report an issue: GitHub.
Appendix: source
Thrown at console/backend/commons/src/main/java/com/iflytek/astron/console/commons/data/impl/UserInfoDataServiceImpl.java:177
if (userInfo.getUid() == null) {
throw new IllegalArgumentException("User UID cannot be null");
}
// First check: fail fast to avoid unnecessary lock contention
Optional<UserInfo> existingUser = findByUid(userInfo.getUid());
if (existingUser.isPresent()) {
return existingUser.get();
}
String lockKey = "user:create:uid:" + userInfo.getUid();
RLock lock = redissonClient.getLock(lockKey);
try {
// Attempt to acquire the lock: wait up to 5s, hold up to 10s
boolean acquired = lock.tryLock(5, 10, TimeUnit.SECONDS);
if (!acquired) {
throw new IllegalStateException("Timed out acquiring distributed lock, please try again later");
}
try {
// Second check: re-validate whether UID exists inside the lock
Optional<UserInfo> existingUserInLock = findByUid(userInfo.getUid());
if (existingUserInLock.isPresent()) {
return existingUserInLock.get();
}
// Set default values
LocalDateTime now = LocalDateTime.now();
if (userInfo.getCreateTime() == null) {
userInfo.setCreateTime(now);
}
if (userInfo.getUpdateTime() == null) {
userInfo.setUpdateTime(now);
}
if (userInfo.getDeleted() == null) {View on GitHub (pinned to 5e758547a8)