{"record":{"id":"94e633b2237268c2","repo":"alibaba/nacos","slug":"lock-interrupted","errorCode":null,"errorMessage":"Lock interrupted","messagePattern":"Lock interrupted","errorType":"exception","errorClass":"NacosLockException","httpStatus":null,"severity":"error","filePath":"client/src/main/java/com/alibaba/nacos/client/lock/NacosLock.java","lineNumber":167,"sourceCode":"                grpcClient.registerForNotification(key, currentOwner());\n                LockResult result = grpcClient.lockWithResult(instance);\n                if (result.isSuccess()) {\n                    grpcClient.cancelWait(key, currentOwner());\n                    localReentrantCount.set(localReentrantCount.get() + 1);\n                    if (result.getReentrantCount() == 1) {\n                        watchdog.register(key, grpcClient, instance);\n                    }\n                    return;\n                }\n                firstAttempt = false;\n                grpcClient.waitForNotification(key, currentOwner(), NOTIFICATION_POLL_TIMEOUT_MS);\n            } catch (InterruptedException e) {\n                // Clear interrupt flag so gRPC cancel request doesn't throw,\n                // then restore it after the synchronous server-side cleanup.\n                grpcClient.cancelWait(key, lockType, currentOwner());\n                Thread.currentThread().interrupt();\n                localReentrantCount.remove();\n                throw new NacosLockException(\"Lock interrupted\", e);\n            } catch (NacosException e) {\n                LOGGER.error(\"Failed to acquire lock, key={}\", key, e);\n                grpcClient.cancelWait(key, lockType, currentOwner());\n                localReentrantCount.remove();\n                throw new NacosLockException(\"Failed to acquire lock: \" + key, e);\n            }\n        }\n    }\n    \n    @Override\n    public void lockInterruptibly() throws InterruptedException {\n        checkReentrantGuard();\n        boolean firstAttempt = true;\n        while (true) {\n            if (Thread.interrupted()) {\n                if (!firstAttempt) {\n                    grpcClient.cancelWait(key, lockType, currentOwner());\n                }","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/client/src/main/java/com/alibaba/nacos/client/lock/NacosLock.java#L149-L185","documentation":"Thrown by NacosLock.lock() when the thread is interrupted while waiting for lock acquisition (InterruptedException caught in the polling loop). The method cancels the server-side wait, restores the interrupt flag, cleans up the ThreadLocal reentrant count, and wraps the cause in a NacosLockException with message \"Lock interrupted\". This makes the blocking lock() interruptible even though it does not declare InterruptedException.","triggerScenarios":"Thread.interrupt() is called on a thread blocked in NacosLock.lock() while it is polling waitForNotification in the acquisition loop. This can happen from application shutdown hooks, timeout-based watchdogs, or framework-managed thread pools that interrupt idle threads.","commonSituations":"A thread pool (e.g. Tomcat, Spring TaskExecutor) interrupts the thread on shutdown while it is blocked acquiring a lock; an application-level timeout mechanism interrupts the thread after N seconds; a Future.cancel(true) is called on a task that is inside lock(); the JVM is shutting down and shutdown hooks interrupt worker threads.","solutions":["If you need interruptible semantics, use lockInterruptibly() instead of lock() — it declares InterruptedException directly.","Catch NacosLockException and check for an InterruptedException cause to handle interruption gracefully.","Use tryLock(timeout, unit) instead of lock() for bounded wait times.","On interruption, decide whether to retry, abort, or propagate — do not ignore it silently."],"exampleFix":"// before\nlock.lock(); // may throw NacosLockException on interrupt\n\n// after — option 1: use interruptible variant\ntry {\n    lock.lockInterruptibly();\n} catch (InterruptedException e) {\n    Thread.currentThread().interrupt();\n    return;\n}\n\n// after — option 2: bounded tryLock\nif (!lock.tryLock(30, TimeUnit.SECONDS)) {\n    throw new TimeoutException(\"Could not acquire lock\");\n}","handlingStrategy":"try-catch","validationCode":"// No pre-validation possible — interruption is asynchronous.\n// Mitigate by using lockInterruptibly() or tryLock(timeout) instead of lock().","typeGuard":"// N/A — interruption is a runtime event, not a type-level concern.","tryCatchPattern":"try {\n    lock.lock();\n} catch (NacosLockException e) {\n    if (e.getCause() instanceof InterruptedException) {\n        Thread.currentThread().interrupt();\n        logger.info(\"Lock acquisition interrupted for key={}\", lock.getKey());\n        // handle gracefully — abort or re-acquire\n    } else {\n        throw e;\n    }\n}","preventionTips":["Prefer lockInterruptibly() when you need to respond to interruption — it declares InterruptedException directly.","Use tryLock(timeout, unit) for bounded waits instead of unbounded lock().","Always restore the interrupt flag (Thread.currentThread().interrupt()) after catching interruption.","Avoid acquiring distributed locks on thread-pool threads that may be interrupted on shutdown."],"tags":["lock","distributed","interruption","concurrency"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}