apache/iceberg · error · RuntimeException
Failed to release lock
Error message
Failed to release lock
What it means
ZkLock.unlock() failed to set the shared counter back to UNLOCKED; after logging a warning it throws RuntimeException('Failed to release lock', e). The lock may remain held in ZooKeeper and block future maintenance triggers.
Source
Thrown at flink/v1.20/flink/src/main/java/org/apache/iceberg/flink/maintenance/api/ZkLockFactory.java:243
return isHeld(sharedCount.getVersionedValue());
}
private static boolean isHeld(VersionedValue<Integer> versionedValue) {
try {
return versionedValue.getValue() == LOCKED;
} catch (Exception e) {
throw new RuntimeException("Failed to check Zookeeper lock status", e);
}
}
@Override
public void unlock() {
try {
sharedCount.setCount(UNLOCKED);
LOG.debug("Released lock for path: {}", lockPath);
} catch (Exception e) {
LOG.warn("Failed to release lock for path: {}", lockPath, e);
throw new RuntimeException("Failed to release lock", e);
}
}
}
@VisibleForTesting
RetryPolicy createRetryPolicy() {
ZKRetryPolicies effectivePolicy =
(retryPolicy == null) ? ZKRetryPolicies.EXPONENTIAL_BACKOFF : retryPolicy;
switch (effectivePolicy) {
case ONE_TIME:
return new RetryOneTime(baseSleepTimeMs);
case N_TIME:
return new RetryNTimes(maxRetries, baseSleepTimeMs);
case BOUNDED_EXPONENTIAL_BACKOFF:
return new BoundedExponentialBackoffRetry(baseSleepTimeMs, maxSleepTimeMs, maxRetries);View on GitHub (pinned to 86d9c8fc54)
Solutions
- Inspect getCause(); if ConnectionLoss/BadVersion, retry unlock() once the session re-establishes.
- Manually reset the counter znode (or delete it) to clear a stuck lock, then let the factory reinitialize it.
- Fix ACLs so the client principal has write access to the lock path.
- Avoid sharing the same lockId across concurrent triggers; give each trigger a unique lockId.
- Use unique lockIds and short session timeouts so expired locks self-release on session close.
Example fix
// before
ZkLockFactory.builder().setLockId("maintenance") // shared by two jobs -> version conflicts
// after
ZkLockFactory.builder().setLockId("maintenance-daily-aggregate") // unique per trigger Defensive patterns
Strategy: retry
Try / catch
try {
lock.unlock();
} catch (RuntimeException e) {
LOG.error("Release failed; lock {} may stay held", lockId, e.getCause());
// retry unlock with backoff, else manually reset the counter znode
} Prevention
- Assign a unique lockId per trigger to avoid version conflicts
- Keep ACL write permissions stable for the client principal
- Retry unlock on transient ConnectionLoss before manual cleanup
- Monitor session expiry and reconnect before unlocking
When it happens
Trigger: sharedCount.setCount(UNLOCKED) throws: session expired (ConnectionLoss), NoAuth on the znode, the versioned set fails because the node changed concurrently, or the znode was deleted externally before the write.
Common situations: ZooKeeper maintenance/failover during unlock; ACLs changed so the client can no longer write; two instances racing on the same lockId causing a BadVersion error; manual znode deletion by an operator.
Related errors
- Failed to release lock for path: {}
- Connection to Zookeeper timed out
- Interrupted while connecting to Zookeeper
- Failed to initialize SharedCount
- Failed to check Zookeeper lock status
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/3496b24d733bdfe5.
Report an issue: GitHub.