apache/iceberg · error · IllegalStateException
Cannot call acquireLock twice for %s
Error message
Cannot call acquireLock twice for %s
What it means
MetastoreLock combines an in-JVM ReentrantLock (per table full name, from a cache) with the metastore lock. acquireJvmLock guards against double-acquisition within one MetastoreLock instance; calling lock() twice on the same instance throws this IllegalStateException. It is an internal-state misuse of the lock object, not a metastore failure.
Source
Thrown at hive-metastore/src/main/java/org/apache/iceberg/hive/MetastoreLock.java:440
Thread.currentThread().interrupt(); // Set back the interrupt status
LOG.warn("Interrupted finding locks to unlock {}.{}", databaseName, tableName, ie);
}
} catch (Exception e) {
LOG.warn("Failed to unlock {}.{}", databaseName, tableName, e);
}
}
private void doUnlock(long lockId) throws TException, InterruptedException {
metaClients.run(
client -> {
client.unlock(lockId);
return null;
});
}
private void acquireJvmLock() {
if (jvmLock != null) {
throw new IllegalStateException(
String.format("Cannot call acquireLock twice for %s", fullName));
}
jvmLock = commitLockCache.get(fullName, t -> new ReentrantLock(true));
jvmLock.lock();
}
private void releaseJvmLock() {
if (jvmLock != null) {
jvmLock.unlock();
jvmLock = null;
}
}
private static void initTableLevelLockCache(long evictionTimeout) {
if (commitLockCache == null) {
synchronized (MetastoreLock.class) {
if (commitLockCache == null) {View on GitHub (pinned to 86d9c8fc54)
Solutions
- Create a fresh MetastoreLock per commit/lock cycle instead of reusing a locked instance.
- If retrying a commit, go through the catalog/table commit API which constructs a new lock each time.
- Guard shared access to the lock object with synchronization or thread confinement so acquireJvmLock runs once per instance.
- Check for double invocation of lock() in custom wrappers around MetastoreLock.
Example fix
// before MetastoreLock lock = lockCache.get(table); lock.lock(); retryCommit(lock); // lock() again on same instance -> IllegalStateException // after MetastoreLock lock = new MetastoreLock(clients, database, table, heartbeatInterval); lock.lock(); commit(); lock.unlock();
Defensive patterns
Strategy: validation
Validate before calling
// never reuse a MetastoreLock across lock cycles
Map<String, MetastoreLock> active = new ConcurrentHashMap<>();
void guard(String table) {
if (active.putIfAbsent(table, newLock(table)) != null) {
throw new IllegalStateException("Lock already active for " + table);
}
} Try / catch
try {
lock.lock();
doCommit();
} catch (IllegalStateException e) {
if (e.getMessage().contains("Cannot call acquireLock twice")) {
// misuse: recreate the lock object for this commit cycle
lock = newLock(table); lock.lock(); doCommit();
} else throw e;
} Prevention
- Construct a fresh MetastoreLock per commit; never cache locked instances
- Retry commits via the table/catalog commit API, which builds new locks
- Confine each MetastoreLock to one thread; don't share via global maps
- Audit custom wrappers for double lock() calls
When it happens
Trigger: Calling lock() (directly or via a second commit path) on the same MetastoreLock instance that already holds its JVM lock — e.g. reusing a cached lock object across retries or sharing the instance between threads without synchronization.
Common situations: Application code caching MetastoreLock instances and re-committing; custom catalog integrations that call lock() in a retry loop on the same object; race where two threads grab the same MetastoreLock from a shared map.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Lock is not active
- Could not acquire the lock on %s.%s, lock request ended in s
- Failed to find lock for table %s.%s
- Failed to acquire lock on file: %s with owner: %s
- Interrupted in call to listTables
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/e6d6864523e53092.
Report an issue: GitHub.