alibaba/nacos · error · IllegalMonitorStateException
Non-reentrant lock does not allow reentry on the same thread
Error message
Non-reentrant lock does not allow reentry on the same thread, key={} What it means
Thrown by NacosLock.checkReentrantGuard() when a NON_REENTRANT lock is acquired a second time on a thread that already holds it (localReentrantCount > 0). This is a deliberate client-side guard: without it, the request would reach the server, which would reject and enqueue it, causing self-deadlock. The exception type is IllegalMonitorStateException (not NacosException).
Source
Thrown at client/src/main/java/com/alibaba/nacos/client/lock/NacosLock.java:133
instance.setLockType(lockType);
instance.setOwner(currentOwner());
instance.setExpiredTime(expiredTime);
return instance;
}
/**
* Guard against non-reentrant lock reentry on the same thread.
*
* <p>For {@code NON_REENTRANT} locks, if the current thread already holds the lock
* ({@code localReentrantCount > 0}), a second acquisition attempt is immediately
* rejected with {@link IllegalMonitorStateException}. Without this client-side check,
* the request would be sent to the server, which rejects it and places the thread in
* the wait queue, causing self-deadlock (the thread waits for itself to release the lock).
*/
private void checkReentrantGuard() {
if (LockConstants.NON_REENTRANT_LOCK_TYPE.equals(lockType)
&& localReentrantCount.get() > 0) {
throw new IllegalMonitorStateException(
"Non-reentrant lock does not allow reentry on the same thread, key=" + key);
}
}
@Override
public void lock() {
checkReentrantGuard();
boolean firstAttempt = true;
while (true) {
try {
LockInstance instance = buildInstance(-1);
instance.setWaitTime(DEFAULT_SERVER_WAIT_TIME_MS);
if (!firstAttempt) {
instance.setWaiterRetry(true);
}
grpcClient.registerForNotification(key, currentOwner());
LockResult result = grpcClient.lockWithResult(instance);
if (result.isSuccess()) {View on GitHub (pinned to 9b989acdf1)
Solutions
- Switch the lock type to REENTRANT if same-thread reentry is expected.
- Restructure the code so the lock is not acquired twice on the same thread — extract the inner logic outside the lock scope.
- Ensure unlock() is called before re-acquiring the lock on the same thread.
- Audit the call chain to find the nested acquisition point and eliminate it.
Example fix
// before
Lock lock = lockService.getLock("my-key", LockConstants.NON_REENTRANT_LOCK_TYPE);
lock.lock();
doWorkThatAlsoLocks(); // throws IllegalMonitorStateException
lock.unlock();
// after
Lock lock = lockService.getLock("my-key", LockConstants.REENTRANT_LOCK_TYPE);
lock.lock();
doWorkThatAlsoLocks(); // reentry succeeds
lock.unlock(); Defensive patterns
Strategy: validation
Validate before calling
void safeLock(NacosLock lock) {
// before acquiring, verify the thread does not already hold the lock
// (for non-reentrant locks)
if (LockConstants.NON_REENTRANT_LOCK_TYPE.equals(lock.getLockType())
&& isHeldByCurrentThread(lock)) {
throw new IllegalStateException(
"Thread already holds non-reentrant lock: " + lock.getKey());
}
lock.lock();
} Type guard
// Use a REENTRANT lock type when same-thread reentry is possible
String chooseLockType(boolean mayReenter) {
return mayReenter
? LockConstants.REENTRANT_LOCK_TYPE
: LockConstants.NON_REENTRANT_LOCK_TYPE;
} Try / catch
try {
lock.lock();
} catch (IllegalMonitorStateException e) {
if (e.getMessage().contains("Non-reentrant")) {
// switch to reentrant or restructure to avoid nested acquisition
logger.error("Reentrant acquisition on non-reentrant lock: {}", lock.getKey());
}
} Prevention
- Choose REENTRANT lock type if any code path may re-enter the lock on the same thread.
- Audit call chains for nested lock acquisitions before using NON_REENTRANT.
- Restructure inner methods to not acquire the lock when called from a locked context.
- Document lock type expectations at the lock creation site.
When it happens
Trigger: Calling lock(), lockInterruptibly(), tryLock(), or tryLock(time, unit) on a NON_REENTRANT NacosLock instance that the current thread has already acquired and not yet released. Common in code that wraps the lock call in a method that is itself called from within a locked region.
Common situations: A service method acquires the lock, then calls another method that also tries to acquire the same lock key on the same thread; refactoring moved code that was previously outside a locked block into a locked block; using a non-reentrant lock type where a reentrant one was intended; the lock type was configured incorrectly (NON_REENTRANT instead of REENTRANT).
Related errors
- Recursive unlock() detected for key={}
- Lock interrupted
- Failed to acquire lock: {}
- Current thread does not hold the lock
- Unlock rejected by server, key={}, msg={}
AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14).
Data as JSON: /api/errors/d39487dc4c56be77.
Report an issue: GitHub.