apache/iceberg · error · UncheckedSQLException

Failed to remove lock

Error message

Failed to remove lock %s

What it means

unlock() in JdbcLockFactory hits a SQLException while reading or updating the lock rows in the JDBC catalog table, so the lock could not be removed. The original SQLException is wrapped in UncheckedSQLException and includes the lock's toString() (type and lockId) for diagnosis.

Solutions

  1. Check the wrapped SQLException cause for the root DB error (connectivity, missing table, permissions)
  2. Verify the JDBC connection is healthy and the lock table exists with the expected schema
  3. Retry the maintenance cycle; unlock failure typically leaves the lock for the next attempt
  4. Increase JDBC connection/validation timeouts if transient network issues are the cause

Example fix

// before
lockFactory.tryLock(); // UncheckedSQLException: Failed to remove lock
// after
try {
  lockFactory.tryLock();
} catch (UncheckedSQLException e) {
  LOG.error("Lock removal failed for {}", e.getCause(), e); // inspect root SQLException
}
Defensive patterns

Strategy: retry

Validate before calling

// verify DB reachability before maintenance
try (Connection c = DriverManager.getConnection(jdbcUrl, user, pass)) { c.isValid(5); }

Try / catch

try { lock.unlock(); } catch (UncheckedSQLException e) { LOG.error("unlock failed: {}", e.getCause()); /* retry next cycle */ }

Prevention

When it happens

Trigger: Calling JdbcLock.unlock() when the underlying SQL statements to fetch/update/delete the lock row fail: connection loss, table missing, constraint violation, or DB timeout.

Common situations: Database restarted or connection pool closed mid-maintenance; lock table schema mismatch after catalog upgrade; network partition to JDBC metastore; wrong jdbc catalog config pointing to a database without the lock table.

Related errors


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

Appendix: source

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

                  sql.setString(2, lockId);
                  sql.setString(3, instanceId);
                  long count = sql.executeUpdate();
                  LOG.info(
                      "Deleted {} lock with instanceId {} with row count {}",
                      this,
                      instanceId,
                      count);
                }

                return null;
              });
        }
      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new UncheckedInterruptedException(e, "Interrupted during unlock");
      } catch (SQLException e) {
        // SQL exception happened when getting/updating lock information
        throw new UncheckedSQLException(e, "Failed to remove lock %s", this);
      }
    }

    @Override
    public String toString() {
      return MoreObjects.toStringHelper(this).add("type", type).add("lockId", lockId).toString();
    }

    @SuppressWarnings("checkstyle:NestedTryDepth")
    private String instanceId() {
      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()) {
                  if (rs.next()) {

View on GitHub (pinned to 86d9c8fc54)