apache/seatunnel · error
recovery after rollback prepared transaction failure also fa
Error message
recovery after rollback prepared transaction failure also failed, xid={} What it means
Logged by tryRecoverPreparedTransactionsAfterRollbackFailure when the fallback XA recovery itself throws: both the direct rollback of the prepared transaction and the recoverAndRollback sweep have failed. The recovery exception is attached as suppressed to the pending JdbcConnectorException, which is then thrown, so the transaction likely remains in-doubt on the database until manual or later recovery.
Source
Thrown at seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/sink/JdbcExactlyOnceSinkWriter.java:304
String.format(
"failed to rollback prepared transaction after begin next transaction failure, xid=%s",
xid),
rollbackException);
rollbackFailure.addSuppressed(beginTxException);
tryRecoverPreparedTransactionsAfterRollbackFailure(xid, rollbackFailure);
throw rollbackFailure;
}
}
private void tryRecoverPreparedTransactionsAfterRollbackFailure(
Xid failedRollbackXid, JdbcConnectorException rollbackFailure) {
try {
LOG.warn(
"rollback prepared transaction failed, try to recover pending transactions for current subtask, xid={}",
failedRollbackXid);
xaGroupOps.recoverAndRollback(context, sinkcontext, xidGenerator, null);
} catch (Exception recoveryException) {
LOG.warn(
"recovery after rollback prepared transaction failure also failed, xid={}",
failedRollbackXid,
recoveryException);
rollbackFailure.addSuppressed(recoveryException);
}
}
private void failAndRollbackCurrentXidQuietly() {
if (currentXid == null || !xaFacade.isOpen()) {
return;
}
Xid xid = currentXid;
try {
LOG.debug("remove current transaction, xid={}", xid);
xaFacade.failAndRollback(xid);
} catch (Exception e) {
LOG.warn("unable to fail/rollback current transaction, xid={}", xid, e);
} finally {View on GitHub (pinned to cf67b549a7)
Solutions
- Read the full thrown JdbcConnectorException: beginTxException + rollbackFailure + suppressed recoveryException pinpoint the root cause (usually connectivity)
- Restore DB connectivity, then restart the job — startup recovery will sweep and resolve in-doubt prepared transactions
- Grant recovery privileges: MySQL 8 requires XA_RECOVER_ADMIN for XA RECOVER; verify with a manual 'xa recover'
- Manually resolve in-doubt transactions with database tooling (xa rollback '<xid>' in MySQL, COMMIT FORCE/ROLLBACK FORCE in Oracle) to avoid locks and orphaned transactions
- Check dialect XA support and driver version; some drivers mis-report recovery lists — upgrade to a driver with solid XA support
Example fix
// before: user without recovery privilege CREATE USER 'st'@'%' IDENTIFIED BY '***'; GRANT SELECT, INSERT, UPDATE ON db.* TO 'st'@'%'; // after CREATE USER 'st'@'%' IDENTIFIED BY '***'; GRANT SELECT, INSERT, UPDATE ON db.* TO 'st'@'%'; GRANT XA_RECOVER_ADMIN ON *.* TO 'st'@'%';
Defensive patterns
Strategy: try-catch
Validate before calling
// verify recovery listing works with the configured account BEFORE running production jobs -- MySQL: xa recover; (needs XA_RECOVER_ADMIN) -- Oracle: select * from dba_pending_transactions; -- PostgreSQL: select gid from pg_prepared_xacts;
Try / catch
try {
writer.prepareCommit();
} catch (JdbcConnectorException e) {
// contains beginTxException + rollbackFailure + suppressed recoveryException
for (Throwable t : e.getSuppressed()) {
LOG.error("recovery also failed, resolve in-doubt xids manually", t);
}
// restore connectivity, then restart so startup recovery retries
} Prevention
- Grant XA_RECOVER_ADMIN (MySQL 8) or equivalent recovery privileges to the connector user
- Alert on pg_prepared_xacts / xa recover listing growth — orphaned in-doubt transactions hold locks
- Keep DB connectivity stable (keepalive, sane timeouts) so both rollback and recovery paths stay reachable
- Use a driver/dialect with well-tested XA recovery support; upgrade old JDBC drivers
- Document a manual runbook for resolving in-doubt transactions after prolonged outages
When it happens
Trigger: rollbackPrepareXidOrThrow -> tryRecoverPreparedTransactionsAfterRollbackFailure where xaGroupOps.recoverAndRollback(context, sinkcontext, xidGenerator, null) throws — typically because the connection/facade is still broken, the DB rejects XA RECOVER (missing privileges), or the resource manager is unreachable.
Common situations: Prolonged DB outage during checkpoint; DB account lacking recovery privileges (MySQL XA_RECOVER_ADMIN); XA connection still bound to the dead session; driver/dialect not fully supporting XA recovery listing.
Related errors
- rollback prepared transaction failed, try to recover pending
- rollback {} recovered transactions
- unable to rollback prepared transaction, xid={}
- begin next transaction failed, rollback prepared transaction
- CLASS_NOT_FOUND
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/cee751e439b4eaa4.
Report an issue: GitHub.