apache/shardingsphere · error · BatchAlreadyOpenedException
Batch already opened for statement handle %d
Error message
Batch already opened for statement handle %d
What it means
BatchAlreadyOpenedException thrown by FirebirdBatchCreateCommandExecutor when FirebirdBatchRegistry already holds a batch for the same (connectionId, statementId). Firebird allows only one open batch per statement handle, so a second CREATE BATCH without an intervening FREE must fail.
Source
Thrown at proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/batch/FirebirdBatchCreateCommandExecutor.java:80
// private static final int BLOB_STREAM = 3;
private static final int WIDE_CLUMPLET_LENGTH_SIZE = 4;
private static final int INTEGER_VALUE_LENGTH = 4;
private final FirebirdBatchCreateCommandPacket packet;
private final ConnectionSession connectionSession;
@Override
public Collection<DatabasePacket> execute() throws SQLException {
int connectionId = connectionSession.getConnectionId();
int statementId = packet.getStatementHandle();
if (null == connectionSession.getServerPreparedStatementRegistry().getPreparedStatement(statementId)) {
throw new InvalidStatementHandleException(statementId);
}
if (null != FirebirdBatchRegistry.getInstance().getBatchStatement(connectionId, statementId)) {
throw new BatchAlreadyOpenedException(statementId);
}
ByteBuf batchBlr = packet.getBatchBlr();
int blrLength = batchBlr.readableBytes();
FirebirdParseBatchBlr messageFormat = FirebirdParseBatchBlr.parse(batchBlr, blrLength);
if (messageFormat.getFields().isEmpty()) {
throw new BatchParametersRequiredException(statementId);
}
if (packet.getBatchMessageLength() != messageFormat.getMessageLength()) {
throw new InvalidBatchMessageFormatException(
String.format("invalid message length: computed %d from BLR but client sent %d", messageFormat.getMessageLength(), packet.getBatchMessageLength()));
}
ByteBuf batchParametersBuffer = packet.getBatchParametersBuffer();
BatchParameters batchParameters = BatchParameters.parse(batchParametersBuffer);
FirebirdBatchRegistry.getInstance().registerBatchStatement(connectionId, statementId,
new FirebirdBatchStatement(statementId, messageFormat.getFields(), batchParameters.getBufferSize(), batchParameters.isRecordCounts(), batchParameters.isMultiError()));
return Collections.singleton(new FirebirdGenericResponsePacket().setHandle(statementId));
}
View on GitHub (pinned to e952770a21)
Solutions
- Send CLOSE BATCH (or FREE STATEMENT DROP/UNPREPARE, which also unregisters the batch) before creating a new batch on the same statement handle.
- Make retries idempotent: on 'batch already opened', reuse or close the existing batch instead of blindly re-creating it.
- Audit driver code for error paths that skip batch teardown after a failed EXECUTE BATCH.
Example fix
// before
sendCreateBatch(h); // second time -> BatchAlreadyOpenedException
// after
if (batchOpen(h)) { sendCloseBatch(h); }
sendCreateBatch(h); Defensive patterns
Strategy: validation
Validate before calling
if (openBatches.contains(stmtHandle)) { sendCloseBatch(stmtHandle); openBatches.remove(stmtHandle); }
sendCreateBatch(stmtHandle);
openBatches.add(stmtHandle); Try / catch
catch (SQLException e) {
if (e.getMessage().contains("Batch already opened")) { sendCloseBatch(stmtHandle); sendCreateBatch(stmtHandle); }
else throw e;
} Prevention
- Maintain client-side set of open batch handles.
- Close batches in finally blocks after execute.
When it happens
Trigger: execute() checks FirebirdBatchRegistry.getInstance().getBatchStatement(connectionId, statementId) != null before registering; sending CREATE BATCH twice for the same prepared statement handle, or re-creating a batch after messages were already queued but before CLOSE BATCH/FREE STATEMENT, hits this branch.
Common situations: client retry logic that resends CREATE BATCH after a timeout without first freeing the previous batch; driver bug that does not tear down batch state on error; batch loop reusing a prepared statement for a second batch run without cleanup.
Related errors
- Invalid statement handle: %d
- Invalid batch handle: statement handle %d
- Invalid batch handle: statement handle %d
- Statement used in batch must have parameters
- invalid message length: computed %d from BLR but client sent
AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14).
Data as JSON: /api/errors/e729d42140197b4c.
Report an issue: GitHub.