{"record":{"id":"cd5788b02892f3aa","repo":"apache/dubbo","slug":"interrupted-while-acquiring-read-lock-for-invoker","errorCode":null,"errorMessage":"Interrupted while acquiring read lock for invoker access, cause: {cause}","messagePattern":"Interrupted while acquiring read lock for invoker access, cause: (.+?)","errorType":"exception","errorClass":"RpcException","httpStatus":null,"severity":"error","filePath":"dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java","lineNumber":237,"sourceCode":"                if (!invokerRefreshReadLock.tryLock(LockUtils.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)) {\n                    throw new RpcException(\n                            \"Failed to acquire read lock on invokerRefreshLock within timeout. \" + \"Timeout: \"\n                                    + LockUtils.DEFAULT_TIMEOUT + \"ms, \" + \"Lock state: [readLockHeld=\"\n                                    + invokerRefreshLock.getReadLockCount() + \", writeLockHeld=\"\n                                    + invokerRefreshLock.isWriteLocked() + \", writeLockHeldByCurrentThread=\"\n                                    + invokerRefreshLock.isWriteLockedByCurrentThread() + \"], Service: \"\n                                    + getConsumerUrl().getServiceKey());\n                }\n                lockAcquired = true;\n                // use clone to avoid being modified at doList().\n                if (invokersInitialized) {\n                    availableInvokers = validInvokers.clone();\n                } else {\n                    availableInvokers = invokers.clone();\n                }\n            } catch (InterruptedException e) {\n                Thread.currentThread().interrupt();\n                throw new RpcException(\n                        \"Interrupted while acquiring read lock for invoker access, cause: \" + e.getMessage(), e);\n            } finally {\n                if (lockAcquired) {\n                    invokerRefreshReadLock.unlock();\n                }\n            }\n\n            if (routerChain != null) {\n                singleChain = routerChain.getSingleChain(getConsumerUrl(), availableInvokers, invocation);\n                singleChain.getLock().readLock().lock();\n            }\n            List<Invoker<T>> routedResult = doList(singleChain, availableInvokers, invocation);\n            if (routedResult.isEmpty()) {\n                // 2-2 - No provider available.\n\n                logger.warn(\n                        CLUSTER_NO_VALID_PROVIDER,\n                        \"provider server or registry center crashed\",","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/apache/dubbo/blob/3a3043227f5571d25eb2889de5bca22f2914843b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java#L219-L255","documentation":"Thrown by AbstractDirectory.list() when the thread waiting on invokerRefreshReadLock.tryLock(...) is interrupted (InterruptedException). The interrupt status is re-set (Thread.currentThread().interrupt()) and the call aborts with RpcException. This is the expected outcome when the calling thread is cancelled/interrupted during an RPC.","triggerScenarios":"The RPC-calling thread is interrupted while blocked waiting for the directory read lock — e.g., the RPC timed out at a higher layer and the waiting thread was interrupted, or the application is shutting down and interrupting worker threads.","commonSituations":"Request timeouts configured shorter than the lock wait; application shutdown interrupting in-flight requests; a servlet/actor framework cancelling the calling thread mid-RPC.","solutions":["Treat interruption as cancellation: ensure the caller propagates/cleans interrupt status and the result is discarded (no retry unless intended).","If spurious, investigate who interrupts the RPC thread (timeout interruParser, executor shutdown) and align timeout settings so the lock wait is not the long pole.","Reduce directory lock contention (see error 4) so the read lock is acquired without blocking."],"exampleFix":"// handle interrupt-driven cancellation at the caller\ntry {\n    result = service.foo();\n} catch (RpcException e) {\n    if (Thread.currentThread().isInterrupted()) {\n        // request was cancelled; do not retry blindly\n        throw new CancellationException(\"rpc cancelled\");\n    }\n    throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Avoid calling from an interrupted thread; clear/honor interrupt status deliberately\nif (Thread.currentThread().isInterrupted()) {\n    // decide: cancel the call, or clear the flag if interruption is stale\n    throw new CancellationException(\"thread already interrupted before rpc\");\n}\nreturn directory.list(invocation);","typeGuard":null,"tryCatchPattern":"try {\n    return directory.list(invocation);\n} catch (RpcException e) {\n    if (Thread.currentThread().isInterrupted()) {\n        // interruption-driven cancellation: do not retry; propagate as cancellation\n        throw new CancellationException(\"rpc interrupted: \" + e.getMessage());\n    }\n    throw e;\n}","preventionTips":["Do not reuse interrupted threads for RPCs; honor cancellation semantics.","Align RPC timeouts so the directory lock wait is not the cause of interruption.","Reduce directory lock contention (see error 4) to avoid blocking waits."],"tags":["cluster","directory","concurrency","interruption"],"backgroundTag":null,"analyzedSha":"3a3043227f5571d25eb2889de5bca22f2914843b","analyzedAt":"2026-08-14T00:43:19.853Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}