apache/shardingsphere · error · InvalidTransactionHandleException

Invalid transaction handle: %d

Error message

Invalid transaction handle: %d

What it means

InvalidTransactionHandleException thrown by FirebirdCommitTransactionCommandExecutor.validateTransactionHandle when the transaction handle in a COMMIT packet is not registered as active for the connection in FirebirdTransactionIdGenerator. The proxy tracks per-connection transaction handles; committing an unknown or already-closed handle is rejected.

Source

Thrown at proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/transaction/FirebirdCommitTransactionCommandExecutor.java:58

    
    private final ConnectionSession connectionSession;
    
    @Override
    public Collection<DatabasePacket> execute() throws SQLException {
        int transactionHandle = packet.getTransactionId();
        validateTransactionHandle(transactionHandle);
        if (!connectionSession.isAutoCommit()) {
            ProxyBackendTransactionManager transactionManager = new ProxyBackendTransactionManager(connectionSession.getDatabaseConnectionManager());
            // TODO add rollback and return exception
            transactionManager.commit();
        }
        FirebirdTransactionIdGenerator.getInstance().closeTransaction(connectionSession.getConnectionId(), transactionHandle);
        return Collections.singleton(new FirebirdGenericResponsePacket());
    }
    
    private void validateTransactionHandle(final int transactionHandle) {
        if (!FirebirdTransactionIdGenerator.getInstance().isTransactionActive(connectionSession.getConnectionId(), transactionHandle)) {
            throw new InvalidTransactionHandleException(transactionHandle);
        }
    }
}

View on GitHub (pinned to e952770a21)

Solutions

  1. Commit each transaction handle exactly once; after commit or rollback, discard the handle and start a new transaction.
  2. On ambiguous timeout errors, do not blind-retry COMMIT; check transaction state (or reconnect) first, since the handle is closed by a successful commit.
  3. Keep transaction start/commit on the same connection/attachment.
Defensive patterns

Strategy: validation

Validate before calling

Set<Long> activeTx = new HashSet<>();
if (!activeTx.contains(trHandle)) throw new IllegalStateException("transaction not active");
sendCommit(trHandle);
activeTx.remove(trHandle);

Try / catch

catch (SQLException e) {
    if (e.getMessage().contains("Invalid transaction handle")) {
        // handle already committed/closed elsewhere: treat as success only if state confirms it
        verifyTransactionClosed(); startNewTransaction();
    } else throw e;
}

Prevention

When it happens

Trigger: execute() calls isTransactionActive(connectionSession.getConnectionId(), transactionHandle) before committing; false when the handle was never opened by a START TRANSACTION, was already committed/rolled back, or belongs to a different connection.

Common situations: double COMMIT (client retries after timeout, first commit succeeded); handle reuse after rollback; two-phase or pooled clients carrying transaction handles across reconnects; client sending COMMIT for a transaction started on another attachment.

Related errors


AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14). Data as JSON: /api/errors/a8ff0e1ba341747d. Report an issue: GitHub.