apache/seatunnel · error · JdbcConnectorException

XA_OPERATION_FAILED

XA_OPERATION_FAILED

Error message

unable to start xa transaction, xid=%s

What it means

beginTx() generates an Xid and calls xaFacade.start(currentXid) to begin an XA transaction; if start fails, the error carries the specific xid and code XA_OPERATION_FAILED. This happens on every new transaction started by the exactly-once writer (initial open and each prepareCommit), so it typically means the database refused the XA START.

Source

Thrown at seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/sink/JdbcExactlyOnceSinkWriter.java:233

                    e);
        } finally {
            outputFormat.close();
            xidGenerator.close();
            currentXid = null;
            prepareXid = null;
        }
    }

    private void beginTx(long txIdHint) throws IOException {
        checkState(currentXid == null, "currentXid not null");
        long txId = nextTxId(txIdHint);
        currentXid = xidGenerator.generateXid(context, sinkcontext, txId);
        try {
            xaFacade.start(currentXid);
        } catch (Exception e) {
            Xid xid = currentXid;
            currentXid = null;
            throw new JdbcConnectorException(
                    JdbcConnectorErrorCode.XA_OPERATION_FAILED,
                    String.format("unable to start xa transaction, xid=%s", xid),
                    e);
        }
    }

    private long nextTxId(long txIdHint) {
        long candidate = txIdHint;
        if (candidate <= lastGeneratedTxId) {
            checkState(lastGeneratedTxId != Long.MAX_VALUE, "tx id exhausted");
            candidate = lastGeneratedTxId + 1;
        }
        lastGeneratedTxId = candidate;
        return candidate;
    }

    private void rollbackPrepareXidQuietly() {
        if (prepareXid == null || !xaFacade.isOpen()) {

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Read the xid in the message and the cause: XAER_RMFAIL => reconnectivity issue; XAER_DUPID => xid collision
  2. Fix network/keepalive settings so the connection survives between checkpoints (e.g. TCP keepalive, connection pool validation)
  3. Ensure the XidGenerator produces unique xids per job/restart (check job id/seed configuration)
  4. Confirm the DB supports XA and max_prepared_transactions (PostgreSQL) is > 0

Example fix

// before
xaFacade.start(currentXid) // fails: connection idle-dead after long checkpoint interval
// after
jdbc_url += "?tcpKeepAlive=true&autoReconnect=false" // and reduce checkpoint interval
Defensive patterns

Strategy: retry

Validate before calling

// verify XA start capability
try (XAConnection xa = xaDs.getXAConnection(); XAResource r = xa.getXAResource()) {
    Xid test = new XidImpl(); r.start(test, XAResource.TMNOFLAGS); r.end(test, XAResource.TMSUCCESS); r.forget(test);
}

Try / catch

catch (JdbcConnectorException e) { if (String.valueOf(e.getCause()).contains("XAER_DUPID")) fixXidGenerator(); else retryWithBackoff(); }

Prevention

When it happens

Trigger: beginTx is invoked by tryOpen (first transaction) and prepareCommit (each checkpoint interval); xaFacade.start fails when the underlying connection cannot start the XA transaction (connection dead, DB rejecting XA, duplicate xid).

Common situations: Connection killed by idle timeout between checkpoints; MySQL 'XAER_DUPID' from xid collision (weak xidGenerator across restarted jobs); database XA unsupported or misconfigured; DB outage at checkpoint time.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/d8a225d33250348a. Report an issue: GitHub.