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

  1. Inspect the server error message embedded in the exception (result.getErrorMessage()) to determine whether it was an ownership mismatch, already-released, or expiry.
  2. Ensure the watchdog renewal mechanism is healthy (check logs for renewal failures) before relying on the lock to still be held.
  3. Set an appropriate expiredTime/lease and avoid holding the lock far beyond it; prefer the JUC lock()/lockInterruptibly() path which auto-renews.
  4. 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

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


AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14). Data as JSON: /api/errors/3ff9a03f6c3ab9b7. Report an issue: GitHub.