{"record":{"id":"d39487dc4c56be77","repo":"alibaba/nacos","slug":"non-reentrant-lock-does-not-allow-reentry-on-the-s","errorCode":null,"errorMessage":"Non-reentrant lock does not allow reentry on the same thread, key={}","messagePattern":"Non-reentrant lock does not allow reentry on the same thread, key=(.+?)","errorType":"exception","errorClass":"IllegalMonitorStateException","httpStatus":null,"severity":"error","filePath":"client/src/main/java/com/alibaba/nacos/client/lock/NacosLock.java","lineNumber":133,"sourceCode":"        instance.setLockType(lockType);\n        instance.setOwner(currentOwner());\n        instance.setExpiredTime(expiredTime);\n        return instance;\n    }\n    \n    /**\n     * Guard against non-reentrant lock reentry on the same thread.\n     *\n     * <p>For {@code NON_REENTRANT} locks, if the current thread already holds the lock\n     * ({@code localReentrantCount > 0}), a second acquisition attempt is immediately\n     * rejected with {@link IllegalMonitorStateException}. Without this client-side check,\n     * the request would be sent to the server, which rejects it and places the thread in\n     * the wait queue, causing self-deadlock (the thread waits for itself to release the lock).\n     */\n    private void checkReentrantGuard() {\n        if (LockConstants.NON_REENTRANT_LOCK_TYPE.equals(lockType)\n            && localReentrantCount.get() > 0) {\n            throw new IllegalMonitorStateException(\n                \"Non-reentrant lock does not allow reentry on the same thread, key=\" + key);\n        }\n    }\n    \n    @Override\n    public void lock() {\n        checkReentrantGuard();\n        boolean firstAttempt = true;\n        while (true) {\n            try {\n                LockInstance instance = buildInstance(-1);\n                instance.setWaitTime(DEFAULT_SERVER_WAIT_TIME_MS);\n                if (!firstAttempt) {\n                    instance.setWaiterRetry(true);\n                }\n                grpcClient.registerForNotification(key, currentOwner());\n                LockResult result = grpcClient.lockWithResult(instance);\n                if (result.isSuccess()) {","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/client/src/main/java/com/alibaba/nacos/client/lock/NacosLock.java#L115-L151","documentation":"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).","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before\nLock lock = lockService.getLock(\"my-key\", LockConstants.NON_REENTRANT_LOCK_TYPE);\nlock.lock();\ndoWorkThatAlsoLocks(); // throws IllegalMonitorStateException\nlock.unlock();\n// after\nLock lock = lockService.getLock(\"my-key\", LockConstants.REENTRANT_LOCK_TYPE);\nlock.lock();\ndoWorkThatAlsoLocks(); // reentry succeeds\nlock.unlock();","handlingStrategy":"validation","validationCode":"void safeLock(NacosLock lock) {\n    // before acquiring, verify the thread does not already hold the lock\n    // (for non-reentrant locks)\n    if (LockConstants.NON_REENTRANT_LOCK_TYPE.equals(lock.getLockType())\n        && isHeldByCurrentThread(lock)) {\n        throw new IllegalStateException(\n            \"Thread already holds non-reentrant lock: \" + lock.getKey());\n    }\n    lock.lock();\n}","typeGuard":"// Use a REENTRANT lock type when same-thread reentry is possible\nString chooseLockType(boolean mayReenter) {\n    return mayReenter\n        ? LockConstants.REENTRANT_LOCK_TYPE\n        : LockConstants.NON_REENTRANT_LOCK_TYPE;\n}","tryCatchPattern":"try {\n    lock.lock();\n} catch (IllegalMonitorStateException e) {\n    if (e.getMessage().contains(\"Non-reentrant\")) {\n        // switch to reentrant or restructure to avoid nested acquisition\n        logger.error(\"Reentrant acquisition on non-reentrant lock: {}\", lock.getKey());\n    }\n}","preventionTips":["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."],"tags":["lock","distributed","non-reentrant","reentrancy","client-guard"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}