alibaba/nacos · error · IllegalMonitorStateException
Unlock rejected by server, key={}, msg={}
Error message
Unlock rejected by server, key={}, msg={} What it means
Thrown by NacosLock.unlock() when the server's RELEASE operation returns a non-success LockResult. The client sends an unlock request via grpcClient.unLockWithResult(instance); if result.isSuccess() is false, it clears local state (resets reentrant count, unregisters the watchdog) and then throws IllegalMonitorStateException carrying the server's error message. The local state is force-cleared so the lock does not become permanently unusable, but the server-side error (e.g. ownership mismatch, already released, expired) is surfaced.
Source
Thrown at client/src/main/java/com/alibaba/nacos/client/lock/NacosLock.java:314
if (count <= 0) {
throw new IllegalMonitorStateException("Current thread does not hold the lock");
}
try {
LockInstance instance = buildInstance(0);
LockResult result = grpcClient.unLockWithResult(instance);
if (result.isSuccess()) {
localReentrantCount.set(count - 1);
if (result.getReentrantCount() == 0) {
watchdog.unregister(key);
localReentrantCount.remove();
removed = true;
}
} else {
localReentrantCount.set(0);
watchdog.unregister(key);
localReentrantCount.remove();
removed = true;
throw new IllegalMonitorStateException(
"Unlock rejected by server, key=" + key + ", msg="
+ result.getErrorMessage());
}
} catch (NacosException e) {
// Server may have already released the lock — clear client state
// to prevent the lock from becoming permanently unusable.
localReentrantCount.set(0);
watchdog.unregister(key);
localReentrantCount.remove();
removed = true;
LOGGER.error("Failed to unlock, key={}", key, e);
throw new IllegalStateException("Failed to unlock: " + key, e);
}
} finally {
inUnlock.remove();
if (!removed && localReentrantCount.get() <= 0) {
localReentrantCount.remove();
}View on GitHub (pinned to 9b989acdf1)
Solutions
- Inspect the server error message embedded in the exception (result.getErrorMessage()) to determine whether it was an ownership mismatch, already-released, or expiry.
- Ensure the watchdog renewal mechanism is healthy (check logs for renewal failures) before relying on the lock to still be held.
- Set an appropriate expiredTime/lease and avoid holding the lock far beyond it; prefer the JUC lock()/lockInterruptibly() path which auto-renews.
- Treat this exception as non-fatal for local state — local client state is already cleared, so a retry with a fresh lock() cycle is safe.
Example fix
// before
lock.unlock(); // propagates IllegalMonitorStateException on server reject
// after
try {
lock.unlock();
} catch (IllegalMonitorStateException e) {
LOGGER.warn("Server rejected unlock for key={}, local state already cleared: {}", key, e.getMessage());
// local reentrant count and watchdog already reset by NacosLock
} Defensive patterns
Strategy: try-catch
Try / catch
try { lock.unlock(); } catch (IllegalMonitorStateException e) { LOGGER.warn("Server rejected unlock ({}); local state already cleared", e.getMessage()); } Prevention
- Monitor watchdog renewal logs — silent renewal failures lead to server-side expiry before unlock.
- Set a lease time appropriate to the critical section length.
- Treat server-rejected unlock as non-fatal: local state is already cleared by NacosLock.
When it happens
Trigger: The lock lease already expired on the server (watchdog renewal failed silently) before unlock() is called; another client/owner holds or already released the same key; server-side lock metadata was evicted or corrupted; using a stale LockInstance whose owner token no longer matches the current server state.
Common situations: Network partitions that silently kill watchdog renewals, causing the server to expire the lease while the client still believes it holds the lock; clock skew between client and server pushing expiredTime into the past; calling unlock long after the operation completed; multiple NacosLockService instances or clients targeting the same key with different owner tokens.
Related errors
- Current thread does not hold the lock
- Failed to unlock: {}
- Condition not supported in Nacos distributed lock
- Non-reentrant lock does not allow reentry on the same thread
- Lock interrupted
AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14).
Data as JSON: /api/errors/3ff9a03f6c3ab9b7.
Report an issue: GitHub.