apache/iceberg · error · UncheckedSQLException

Failed to create lock

Error message

Failed to create %s lock

What it means

After a SQLException during lock-row insertion, tryLock checks whether the lock was actually created behind the scenes by comparing instance IDs; if the lock row belongs to a different instance (i.e. insertion genuinely failed and was not a duplicate), it throws UncheckedSQLException with "Failed to create %s lock" (%s formatted with the lock object). It means another owner may hold the lock or the insert failed for a real SQL reason.

Solutions

  1. Check who currently holds the lock (query the lock table's instance/creation columns) and wait for it to be released.
  2. Remove stale lock rows left behind by crashed jobs after confirming no active owner.
  3. Inspect the wrapped SQLException cause for permission/constraint errors on the lock table.
  4. Ensure each job uses a distinct lock ID to avoid cross-job contention.

Example fix

// before (stale row blocks creation)
DELETE nothing; tryLock fails
// after (after confirming owner is dead)
DELETE FROM iceberg_lock WHERE name = 'maintenance' AND instance_id = 'dead-job-instance';
Defensive patterns

Strategy: try-catch

Validate before calling

// check lock ownership before attempting
SELECT instance_id, created_at FROM iceberg_lock WHERE name = ?;

Try / catch

try {
  if (!lock.tryLock()) { /* handle contention */ }
} catch (UncheckedSQLException e) {
  // inspect cause; check the lock table for a stale/foreign owner
  // clean stale rows only after confirming the owner is dead
}

Prevention

When it happens

Trigger: JdbcLock.tryLock when the INSERT raises SQLException AND instanceId() returns an ID different from newInstanceId — i.e. the lock row exists but was created by another process, or the insert failed for a non-duplicate-key reason.

Common situations: Two Flink jobs/maintenance triggers contending for the same lock ID, stale lock rows left by a crashed job, or DB constraint/permission errors on insert.

Related errors


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

Appendix: source

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

                int count = sql.executeUpdate();
                LOG.info(
                    "Created {} lock with instanceId {} with row count {}",
                    this,
                    newInstanceId,
                    count);
                return count == 1;
              }
            });
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new UncheckedInterruptedException(e, "Interrupted during tryLock");
      } catch (SQLException e) {
        // SQL exception happened when creating the lock. Check if the lock creation was
        // successful behind the scenes.
        if (newInstanceId.equals(instanceId())) {
          return true;
        } else {
          throw new UncheckedSQLException(e, "Failed to create %s lock", this);
        }
      }
    }

    @SuppressWarnings("checkstyle:NestedTryDepth")
    @Override
    public boolean isHeld() {
      try {
        return pool.run(
            conn -> {
              try (PreparedStatement sql = conn.prepareStatement(GET_LOCK_SQL)) {
                sql.setString(1, type.key);
                sql.setString(2, lockId);
                try (ResultSet rs = sql.executeQuery()) {
                  return rs.next();
                }
              }
            });

View on GitHub (pinned to 86d9c8fc54)