apache/iceberg · error · UncheckedSQLException
Failed to create %s lock
Error message
Failed to create %s lock
What it means
When JdbcLock.tryLock's INSERT throws SQLException, the code checks whether the lock was created anyway (by comparing the stored instance ID). If the stored instanceId does not match this instance's newInstanceId, the lock genuinely could not be created, and the SQLException is wrapped as UncheckedSQLException with 'Failed to create %s lock'.
Source
Thrown at flink/v1.20/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)
Solutions
- Check the lock table contents: if a stale row from a dead job exists, remove it or let the maintenance recovery procedure reset the lock.
- Verify DB connectivity and fix any SQLException surfaced in the cause chain.
- Ensure only one trigger job with this lock ID runs concurrently.
- Recreate the lock table if its schema drifted from what the current version expects.
Example fix
// stale row cleanup (example for MySQL) // DELETE FROM iceberg_triggers_lock WHERE trigger_id = 'my-table' AND instance_id != '<current>';
Defensive patterns
Strategy: try-catch
Validate before calling
// before acquiring, check who holds the lock
String holder = fetchInstanceId(lockId);
if (holder != null && !holder.equals(myInstanceId)) {
throw new IllegalStateException("Lock held by instance: " + holder);
} Try / catch
try {
acquired = lock.tryLock();
} catch (UncheckedSQLException e) {
// inspect cause: constraint violation vs connection loss
LOG.error("tryLock failed", e.getCause());
// check for stale rows and clean up per runbook, then retry once
} Prevention
- Ensure only one trigger job per lock ID runs concurrently
- Clean up stale lock rows from crashed jobs
- Monitor DB connectivity to avoid mid-INSERT failures
- Keep the lock table schema in sync with the Iceberg version
When it happens
Trigger: tryLock fails when the INSERT errors and the lock row in the maintenance lock table holds a different instance ID — meaning another holder owns the row or the row is stale/corrupt, or the SQL failure is a real error (constraint violation, connection drop).
Common situations: Stale lock rows left by a crashed job with a different instance ID; DB connectivity dropping mid-INSERT; schema/constraint mismatches on the lock table.
Related errors
- Failed to check the state of the lock %s
- Failed to get lock information for %s
- Cannot initialize JDBC table maintenance lock: Query timed o
- Cannot initialize JDBC table maintenance lock: Connection fa
- Cannot initialize JDBC table maintenance lock
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/9b458fb6236040d2.
Report an issue: GitHub.