iflytek/astron-agent · warning
ExecuteShutdown unlock threw exception. key=
Error message
ExecuteShutdown unlock threw exception. key={}, tokenTail=***{} What it means
In ExecuteShutdown.onShutdown, the distributed Redis lock held by the shutdown hook is released via redisUtil.unlock(LOCK_KEY, token). If the unlock call itself throws (Redis connection down at JVM shutdown, serialization issue, etc.), this warning is logged with the key and the last-4 chars of the token, and the lock is left to expire via its TTL rather than being released. The shutdown still completes.
Solutions
- Rely on the lock's TTL: since unlock failed the lock will expire on its own; verify the LOCK_KEY TTL is short enough that stale holds don't block the next ExecuteShutdown run.
- Check why Redis was unavailable at shutdown — connection pool shutdown ordering in Spring context (add a DependsOn / close the Redis client after this hook), or network policy.
- Make unlock retry once with a short backoff inside the hook, or use Redisson's lock.unlock() (idempotent, with built-in Lua token check) instead of a hand-rolled unlock.
Defensive patterns
Strategy: retry
Validate before calling
if (!redisUtil.hasKey(LOCK_KEY)) {
log.info("lock already gone; skip unlock");
} Try / catch
try {
if (!redisUtil.unlock(LOCK_KEY, token)) {
log.warn("unlock rejected; lock will expire via TTL, key={}", LOCK_KEY);
}
} catch (Exception e) {
log.warn("unlock failed at shutdown; relying on TTL, key={}", LOCK_KEY, e);
} Prevention
- Always set a TTL on shutdown locks so failed unlocks self-heal.
- Order Spring bean destruction so the Redis connection factory closes after shutdown hooks run.
- Monitor this warning rate; a spike means Redis connectivity issues during deployments.
When it happens
Trigger: Redis is unreachable or times out when the shutdown hook runs; redisUtil.unlock throws (JedisConnectionException, pool exhaustion) inside the finally block of onShutdown.
Common situations: Kubernetes pod termination during a Redis failover; Redis connection pool already closed by earlier shutdown-hook ordering; network partition between app and Redis at SIGTERM.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- RELEASE_FAILED
- ExecuteShutdown unlock not released (maybe expired or token…
- REDIS_CONNECTION_ERROR
- Timed out acquiring distributed lock, please try again later
- Distributed lock acquisition timeout, please try again later
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/f6e05e438749add4.
Report an issue: GitHub.
Appendix: source
Thrown at console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/config/spring/ExecuteShutdown.java:64
if (!redisUtil.tryLock(LOCK_KEY, LOCK_TTL, token)) {
log.info("ExecuteShutdown skipped: lock already held by another instance. key={}", LOCK_KEY);
return;
}
try {
// Actual shutdown action: Clear canvas hold count
workflowService.removeAllCanvasHold();
log.info("ExecuteShutdown done: removeAllCanvasHold finished.");
} catch (Exception e) {
// Do not block shutdown, but need complete logging
log.error("ExecuteShutdown failed while removing canvas hold.", e);
} finally {
boolean released = false;
try {
released = redisUtil.unlock(LOCK_KEY, token);
} catch (Exception e) {
log.warn("ExecuteShutdown unlock threw exception. key={}, tokenTail=***{}", LOCK_KEY, tail4(token), e);
}
if (!released) {
log.warn("ExecuteShutdown unlock not released (maybe expired or token mismatch). key={}, tokenTail=***{}",
LOCK_KEY, tail4(token));
} else {
log.debug("ExecuteShutdown lock released. key={}", LOCK_KEY);
}
}
}
private boolean shouldSkipByProfile() {
final String[] actives = environment.getActiveProfiles();
if (actives == null || actives.length == 0) {
return false;
}
for (String p : actives) {
for (String skip : SKIP_PROFILES) {
if (skip.equalsIgnoreCase(p)) {View on GitHub (pinned to 5e758547a8)