quarkusio/quarkus · error · jakarta.enterprise.context.control.LockException
Write lock not acquired in ${time} ms
Error message
Write lock not acquired in ${time} ms What it means
When @Lock(WRITE) is used with time > 0, LockInterceptor tries rwl.writeLock().tryLock(time, unit); if the lock is not obtained within that time it throws LockException. The write lock is held by another thread for longer than the configured timeout, so the guarded business method could not run.
Source
Thrown at independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/LockInterceptor.java:61
long time = lock.time();
int readHoldCount = rwl.getReadHoldCount();
boolean locked = false;
try {
if (readHoldCount > 0) {
rl.lock();
}
try {
if (readHoldCount > 0) {
// Release all read locks hold by the current thread before acquiring the write lock
for (int i = 0; i < readHoldCount; i++) {
rwl.readLock().unlock();
}
}
if (time > 0) {
locked = rwl.writeLock().tryLock(time, lock.unit());
if (!locked) {
throw new LockException("Write lock not acquired in " + lock.unit().toMillis(time) + " ms");
}
} else {
rwl.writeLock().lock();
locked = true;
}
} finally {
if (readHoldCount > 0) {
rl.unlock();
}
}
return ctx.proceed();
} finally {
if (locked) {
if (readHoldCount > 0) {
// Re-aqcquire the read locks
for (int i = 0; i < readHoldCount; i++) {
rwl.readLock().lock();
}View on GitHub (pinned to e1c734241f)
Solutions
- Increase lock.time()/unit on the @Lock annotation to a realistic bound.
- Shrink the work performed inside the write-locked method (move slow I/O outside the lock).
- Investigate the competing holder — log or dump threads to find a deadlock or long read-holders.
- Avoid read→write lock upgrade patterns (release the read lock before requesting write).
- If unbounded waiting is acceptable, set time = 0 so writeLock().lock() blocks indefinitely instead of throwing.
Example fix
// before
@Lock(value = LockType.WRITE, time = 100, unit = TimeUnit.MILLISECONDS)
public void sync() { heavyNetworkCall(); }
// after
@Lock(value = LockType.WRITE, time = 5, unit = TimeUnit.SECONDS)
public void sync() { heavyNetworkCall(); } Defensive patterns
Strategy: retry
Validate before calling
// size the timeout to worst-case work: measure the write path before fixing lock.time()
long measuredMs = measure(writeOperation);
if (measuredMs >= unit.toMillis(time)) { log.warn("@Lock(WRITE) time too small: " + measuredMs + "ms"); } Try / catch
try {
return bean.writeOperation();
} catch (LockException e) {
// bounded wait expired: back off and retry or degrade
Thread.sleep(retryBackoffMs);
return bean.writeOperation();
} Prevention
- Keep work inside @Lock(WRITE) methods short and free of slow I/O
- Set generous-but-bounded lock.time values based on measured worst case
- Watch for read→write lock upgrade patterns that self-deadlock
- Under load, monitor lock contention before tightening timeouts
When it happens
Trigger: Method annotated @Lock(value = WRITE, time = N, unit = X) while another thread holds (or waits on) the same bean's write/read lock longer than N units; time > 0 makes the wait bounded and fails fast instead of blocking forever.
Common situations: Long-running write operations under high concurrency; deadlock/livelock between read and write callers; time set too low for slow I/O inside the locked method; same bean called re-entrantly from a thread already holding the read lock (readers cannot upgrade to writer).
Related errors
- Read lock not acquired in ${time} ms
- Unsupported @Lock type found on business method ${method}
- @Lock binding not found on business method ${method}
- Tests already in progress
- The application is stopping
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/20b8fd8a0a191a75.
Report an issue: GitHub.