{"record":{"id":"1f2f6e44b95e40e3","repo":"apache/shardingsphere","slug":"invalid-batch-handle-statement-handle-d-1f2f6e","errorCode":null,"errorMessage":"Invalid batch handle: statement handle %d","messagePattern":"Invalid batch handle: statement handle (.+?)","errorType":"exception","errorClass":"InvalidBatchHandleException","httpStatus":null,"severity":"error","filePath":"proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/batch/FirebirdBatchExecuteCommandExecutor.java","lineNumber":50,"sourceCode":"import org.apache.shardingsphere.proxy.frontend.firebird.command.query.FirebirdServerPreparedStatement;\nimport org.apache.shardingsphere.proxy.frontend.firebird.command.query.transaction.FirebirdTransactionIdGenerator;\n\nimport java.sql.SQLException;\nimport java.util.Collection;\nimport java.util.Collections;\n\n@RequiredArgsConstructor\npublic final class FirebirdBatchExecuteCommandExecutor implements CommandExecutor {\n    \n    private final FirebirdBatchExecuteCommandPacket packet;\n    \n    private final ConnectionSession connectionSession;\n    \n    @Override\n    public Collection<DatabasePacket> execute() throws SQLException {\n        FirebirdBatchStatement batchStatement = FirebirdBatchRegistry.getInstance().getBatchStatement(connectionSession.getConnectionId(), packet.getStatementHandle());\n        if (null == batchStatement) {\n            throw new InvalidBatchHandleException(packet.getStatementHandle());\n        }\n        validateTransactionHandle();\n        int messageCount = batchStatement.getParameterValues().size();\n        if (batchStatement.getParameterValues().isEmpty()) {\n            batchStatement.reset();\n            return Collections.singleton(new FirebirdBatchCompletionStateResponse()\n                    .setHandle(packet.getStatementHandle())\n                    .setRecordsCount(messageCount)\n                    .setUpdateCounts(new int[0]));\n        }\n        FirebirdServerPreparedStatement preparedStatement = connectionSession.getServerPreparedStatementRegistry().getPreparedStatement(batchStatement.getStatementHandle());\n        FirebirdBatchedStatementsExecutor executor = new FirebirdBatchedStatementsExecutor(connectionSession, preparedStatement, batchStatement.getParameterValues(), batchStatement.isMultiError());\n        FirebirdBatchCompletion completion = executor.executeBatch();\n        batchStatement.reset();\n        return Collections.singleton(createResponse(completion, batchStatement.isRecordCounts()));\n    }\n    \n    private void validateTransactionHandle() {","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/apache/shardingsphere/blob/e952770a215630a3659c75d64369168cd3e26b82/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/batch/FirebirdBatchExecuteCommandExecutor.java#L32-L68","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Follow the sequence PREPARE -> CREATE BATCH -> (MESSAGE)+ -> EXECUTE BATCH on one connection, using the handle from CREATE BATCH.","After any batch error, re-create the batch before retrying, since failure paths may free the batch.","Pin all batch calls to a single connection (no pool hand-off between create and execute)."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (!openBatches.contains(stmtHandle)) throw new IllegalStateException(\"no open batch for \" + stmtHandle);\nsendExecuteBatch(stmtHandle);","typeGuard":null,"tryCatchPattern":"catch (SQLException e) {\n    if (e.getMessage().contains(\"Invalid batch handle\")) { int h = recreateBatch(sql); sendExecuteBatch(h); } else throw e;\n}","preventionTips":["Track open batch handles client-side and clear them on close/free.","Re-create the batch after any error before retrying execute."],"tags":["firebird","batch","statement-handle","protocol-ordering"],"backgroundTag":null,"analyzedSha":"e952770a215630a3659c75d64369168cd3e26b82","analyzedAt":"2026-08-14T13:54:53.392Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}