apache/shardingsphere · error · InvalidBatchHandleException

Invalid batch handle: statement handle %d

Error message

Invalid batch handle: statement handle %d

What it means

InvalidBatchHandleException thrown by FirebirdBatchExecuteCommandExecutor when FirebirdBatchRegistry has no batch registered for (connectionId, statementHandle). EXECUTE BATCH only works after a successful CREATE BATCH (and at least implicitly queued messages or an empty completion state) on the same connection.

Source

Thrown at proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/batch/FirebirdBatchExecuteCommandExecutor.java:50

import org.apache.shardingsphere.proxy.frontend.firebird.command.query.FirebirdServerPreparedStatement;
import org.apache.shardingsphere.proxy.frontend.firebird.command.query.transaction.FirebirdTransactionIdGenerator;

import java.sql.SQLException;
import java.util.Collection;
import java.util.Collections;

@RequiredArgsConstructor
public final class FirebirdBatchExecuteCommandExecutor implements CommandExecutor {
    
    private final FirebirdBatchExecuteCommandPacket packet;
    
    private final ConnectionSession connectionSession;
    
    @Override
    public Collection<DatabasePacket> execute() throws SQLException {
        FirebirdBatchStatement batchStatement = FirebirdBatchRegistry.getInstance().getBatchStatement(connectionSession.getConnectionId(), packet.getStatementHandle());
        if (null == batchStatement) {
            throw new InvalidBatchHandleException(packet.getStatementHandle());
        }
        validateTransactionHandle();
        int messageCount = batchStatement.getParameterValues().size();
        if (batchStatement.getParameterValues().isEmpty()) {
            batchStatement.reset();
            return Collections.singleton(new FirebirdBatchCompletionStateResponse()
                    .setHandle(packet.getStatementHandle())
                    .setRecordsCount(messageCount)
                    .setUpdateCounts(new int[0]));
        }
        FirebirdServerPreparedStatement preparedStatement = connectionSession.getServerPreparedStatementRegistry().getPreparedStatement(batchStatement.getStatementHandle());
        FirebirdBatchedStatementsExecutor executor = new FirebirdBatchedStatementsExecutor(connectionSession, preparedStatement, batchStatement.getParameterValues(), batchStatement.isMultiError());
        FirebirdBatchCompletion completion = executor.executeBatch();
        batchStatement.reset();
        return Collections.singleton(createResponse(completion, batchStatement.isRecordCounts()));
    }
    
    private void validateTransactionHandle() {

View on GitHub (pinned to e952770a21)

Solutions

  1. Follow the sequence PREPARE -> CREATE BATCH -> (MESSAGE)+ -> EXECUTE BATCH on one connection, using the handle from CREATE BATCH.
  2. After any batch error, re-create the batch before retrying, since failure paths may free the batch.
  3. Pin all batch calls to a single connection (no pool hand-off between create and execute).
Defensive patterns

Strategy: validation

Validate before calling

if (!openBatches.contains(stmtHandle)) throw new IllegalStateException("no open batch for " + stmtHandle);
sendExecuteBatch(stmtHandle);

Try / catch

catch (SQLException e) {
    if (e.getMessage().contains("Invalid batch handle")) { int h = recreateBatch(sql); sendExecuteBatch(h); } else throw e;
}

Prevention

When it happens

Trigger: execute() looks up FirebirdBatchRegistry.getInstance().getBatchStatement(connectionSession.getConnectionId(), packet.getStatementHandle()); null means CREATE BATCH was never sent, the batch was closed/freed, or the handle belongs to another connection.

Common situations: client calls executeBatch before addBatch/createBatch completed; batch state was torn down by FREE STATEMENT after an error; connection pool switches physical connections between create and execute; client bug desynchronizing handles after re-prepare.

Related errors


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