{"record":{"id":"063340f97da9ade4","repo":"apache/iceberg","slug":"failed-to-remove-lock-s-063340","errorCode":null,"errorMessage":"Failed to remove lock %s","messagePattern":"Failed to remove lock (.+?)","errorType":"exception","errorClass":"UncheckedSQLException","httpStatus":null,"severity":"error","filePath":"flink/v2.1/flink/src/main/java/org/apache/iceberg/flink/maintenance/api/JdbcLockFactory.java","lineNumber":273,"sourceCode":"                  sql.setString(2, lockId);\n                  sql.setString(3, instanceId);\n                  long count = sql.executeUpdate();\n                  LOG.info(\n                      \"Deleted {} lock with instanceId {} with row count {}\",\n                      this,\n                      instanceId,\n                      count);\n                }\n\n                return null;\n              });\n        }\n      } catch (InterruptedException e) {\n        Thread.currentThread().interrupt();\n        throw new UncheckedInterruptedException(e, \"Interrupted during unlock\");\n      } catch (SQLException e) {\n        // SQL exception happened when getting/updating lock information\n        throw new UncheckedSQLException(e, \"Failed to remove lock %s\", this);\n      }\n    }\n\n    @Override\n    public String toString() {\n      return MoreObjects.toStringHelper(this).add(\"type\", type).add(\"lockId\", lockId).toString();\n    }\n\n    @SuppressWarnings(\"checkstyle:NestedTryDepth\")\n    private String instanceId() {\n      try {\n        return pool.run(\n            conn -> {\n              try (PreparedStatement sql = conn.prepareStatement(GET_LOCK_SQL)) {\n                sql.setString(1, type.key);\n                sql.setString(2, lockId);\n                try (ResultSet rs = sql.executeQuery()) {\n                  if (rs.next()) {","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/flink/v2.1/flink/src/main/java/org/apache/iceberg/flink/maintenance/api/JdbcLockFactory.java#L255-L291","documentation":"Thrown by JdbcLock.unlock when the SQL statements that read/update lock info and delete the lock row fail with a SQLException. The lock could not be released, leaving it possibly held (stale). The original SQL error is wrapped in UncheckedSQLException.","triggerScenarios":"unlock's SELECT/UPDATE/DELETE against the lock table fails: lost connection, lock table missing, insufficient DELETE privilege, or a deadlock/timeout on the lock row.","commonSituations":"DB failover during unlock; maintenance user granted only SELECT/INSERT but not DELETE; network partition; long-running transaction holding the lock row causes timeout.","solutions":["Inspect the wrapped SQLException (getCause) for the exact SQL failure","Grant the maintenance DB user UPDATE/DELETE privileges on the lock table","Check for orphaned/stale lock rows after failure and delete them before the next run","Verify DB connectivity and retry the release; enable connection pool validation to drop dead connections"],"exampleFix":"// before\nGRANT SELECT, INSERT ON iceberg_lock TO maintenance_user; // unlock DELETE fails\n// after\nGRANT SELECT, INSERT, UPDATE, DELETE ON iceberg_lock TO maintenance_user;","handlingStrategy":"retry","validationCode":"// verify DML rights before running maintenance\ntry (Connection c = ds.getConnection()) {\n  c.setAutoCommit(false);\n  try (Statement s = c.createStatement()) {\n    s.execute(\"DELETE FROM iceberg_lock WHERE lock_id = '__probe__'\");\n  }\n  c.rollback();\n} catch (SQLException e) { throw new IllegalStateException(\"No DELETE grant on lock table\"); }","typeGuard":null,"tryCatchPattern":"try {\n  lock.unlock();\n} catch (UncheckedSQLException e) {\n  log.error(\"Lock release failed; manual stale-lock cleanup required\", e);\n  scheduleStaleLockCleanup();\n}","preventionTips":["Grant UPDATE and DELETE on the lock table to the maintenance user, not just SELECT/INSERT","Detect and delete orphaned lock rows from crashed jobs on a schedule","Check the cause chain for deadlock/timeout SQL states and retry those","Avoid DB failovers during maintenance windows; prefer a highly available lock DB endpoint"],"tags":["jdbc","sql","locking","unlock","flink"],"backgroundTag":"database-write-failed","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}