apache/iceberg · error · UncheckedSQLException
Failed to remove lock %s
Error message
Failed to remove lock %s
What it means
JdbcLock.unlock() throws UncheckedSQLException when the SQL used to read/update the lock table fails, so the lock could not be removed. The message includes the lock identity. Because the lock row may still exist, later maintenance triggers can be blocked until the issue is fixed.
Source
Thrown at flink/v2.2/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)
Solutions
- Inspect the wrapped SQLException cause (deadlock, timeout, connection reset) and fix that root cause first.
- Re-run maintenance or manually delete the stale lock row for this lockId from the lock table.
- Add connection retry/validation settings to the JDBC URL and pool so transient drops self-heal.
- Ensure only one instance per lockId runs; row contention often means a duplicate trigger is deployed.
- If the table is missing, re-initialize the JDBC catalog to recreate the lock table.
Example fix
// before String url = "jdbc:postgresql://db:5432/iceberg"; // stale connections // after String url = "jdbc:postgresql://db:5432/iceberg?connectTimeout=10&socketTimeout=60"; // plus: DELETE FROM iceberg_locks WHERE lock_id = 'my-trigger'; // clear orphaned lock
Defensive patterns
Strategy: retry
Validate before calling
try (Connection c = DriverManager.getConnection(jdbcUrl, user, pass)) { c.createStatement().execute("SELECT 1 FROM iceberg_locks LIMIT 1"); } // table reachable and present Try / catch
try { lock.unlock(); } catch (UncheckedSQLException e) { log.error("Unlock failed: {}", e.getCause(), e); /* delete orphaned lock row manually */ } Prevention
- Add socketTimeout/connectTimeout to the JDBC URL so failures surface quickly.
- Monitor for row-lock contention; one trigger instance per lockId.
- Have an operator runbook to delete stale lock rows after failures.
- Re-initialize the catalog if the lock table is missing.
When it happens
Trigger: unlock() executes its get/update lock SQL and the driver throws SQLException: broken connection, missing lock table, deadlocks/lock timeouts on the row, or SQL dialect incompatibility.
Common situations: Network drop between Flink and the database mid-unlock; row-level lock contention with another trigger instance; lock table dropped after a catalog re-creation; database failover invalidating pooled connections.
Related errors
- Failed to check the state of the lock %s
- Failed to remove lock %s
- Failed to get lock information for %s
- Failed to remove lock %s
- Failed to check the state of the lock %s
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/2c471174781f8e9c.
Report an issue: GitHub.