alibaba/nacos · error · NacosLockException
Lock interrupted
Error message
Lock interrupted
What it means
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.
Source
Thrown at client/src/main/java/com/alibaba/nacos/client/lock/NacosLock.java:167
grpcClient.registerForNotification(key, currentOwner());
LockResult result = grpcClient.lockWithResult(instance);
if (result.isSuccess()) {
grpcClient.cancelWait(key, currentOwner());
localReentrantCount.set(localReentrantCount.get() + 1);
if (result.getReentrantCount() == 1) {
watchdog.register(key, grpcClient, instance);
}
return;
}
firstAttempt = false;
grpcClient.waitForNotification(key, currentOwner(), NOTIFICATION_POLL_TIMEOUT_MS);
} catch (InterruptedException e) {
// Clear interrupt flag so gRPC cancel request doesn't throw,
// then restore it after the synchronous server-side cleanup.
grpcClient.cancelWait(key, lockType, currentOwner());
Thread.currentThread().interrupt();
localReentrantCount.remove();
throw new NacosLockException("Lock interrupted", e);
} catch (NacosException e) {
LOGGER.error("Failed to acquire lock, key={}", key, e);
grpcClient.cancelWait(key, lockType, currentOwner());
localReentrantCount.remove();
throw new NacosLockException("Failed to acquire lock: " + key, e);
}
}
}
@Override
public void lockInterruptibly() throws InterruptedException {
checkReentrantGuard();
boolean firstAttempt = true;
while (true) {
if (Thread.interrupted()) {
if (!firstAttempt) {
grpcClient.cancelWait(key, lockType, currentOwner());
}View on GitHub (pinned to 9b989acdf1)
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.
Example fix
// before
lock.lock(); // may throw NacosLockException on interrupt
// after — option 1: use interruptible variant
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
// after — option 2: bounded tryLock
if (!lock.tryLock(30, TimeUnit.SECONDS)) {
throw new TimeoutException("Could not acquire lock");
} Defensive patterns
Strategy: try-catch
Validate before calling
// No pre-validation possible — interruption is asynchronous. // Mitigate by using lockInterruptibly() or tryLock(timeout) instead of lock().
Type guard
// N/A — interruption is a runtime event, not a type-level concern.
Try / catch
try {
lock.lock();
} catch (NacosLockException e) {
if (e.getCause() instanceof InterruptedException) {
Thread.currentThread().interrupt();
logger.info("Lock acquisition interrupted for key={}", lock.getKey());
// handle gracefully — abort or re-acquire
} else {
throw e;
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Recursive unlock() detected for key={}
- Non-reentrant lock does not allow reentry on the same thread
- Failed to acquire lock: {}
- 30000
- 20005
AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14).
Data as JSON: /api/errors/94e633b2237268c2.
Report an issue: GitHub.