apache/iceberg · error · RuntimeException

Failed to check Zookeeper lock status

Error message

Failed to check Zookeeper lock status

What it means

ZkLockFactory's lock compares a versioned SharedCount value against LOCKED to decide whether the lock is held. If reading the value from ZooKeeper throws any Exception (connection loss, session expired, node missing), it is wrapped in RuntimeException("Failed to check Zookeeper lock status"). This prevents treating an unreadable lock state as "unlocked".

Source

Thrown at flink/v2.2/flink/src/main/java/org/apache/iceberg/flink/maintenance/api/ZkLockFactory.java:232

        }

        return acquired;
      } catch (Exception e) {
        LOG.warn("Failed to acquire Zookeeper lock", e);
        return false;
      }
    }

    @Override
    public boolean isHeld() {
      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 =

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Check the wrapped cause for KeeperException types (CONNECTIONLOSS, SESSIONEXPIRED, NONODE) and address that specific problem.
  2. Ensure open() (or factory.create()) was invoked before tryLock; isHeld on an unstarted SharedCount fails.
  3. Verify the znode path for the lockId still exists; recreate it by re-opening the factory if it was deleted.
  4. Stabilize the ZK session (tune session timeout, avoid long GC pauses) and retry the operation with backoff.
  5. Run only one instance per lockId to avoid external interference with the counter znode.

Example fix

// before
Lock lock = factory.createLock(); // open() never called
boolean locked = lock.tryLock(); // -> Failed to check Zookeeper lock status
// after
LockFactory lf = factory.create(); // opens client + SharedCounts
Lock lock = lf.createLock();
boolean locked = lock.tryLock();
Defensive patterns

Strategy: retry

Validate before calling

if (!lockFactory.isInitialized() /* ensure open() ran */) throw new IllegalStateException("Call create()/open() before tryLock");

Try / catch

try { boolean locked = lock.tryLock(); } catch (RuntimeException e) { if ("Failed to check Zookeeper lock status".equals(e.getMessage())) { /* check ZK session, retry with backoff */ } else throw e; }

Prevention

When it happens

Trigger: tryLock or isHeld reads the SharedCount's VersionedValue and the underlying ZK get fails: connection loss, session expiry, the counter znode has not been created/started, or the znode was deleted externally.

Common situations: ZooKeeper session expired after a GC pause or network partition; someone/something deleted the trigger's znode path; open() was never called so the SharedCounts are not running; flaky network between Flink and the ensemble.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/2e3011946a22eaf0. Report an issue: GitHub.