apache/shardingsphere · error · InvalidBatchHandleException
Invalid batch handle: statement handle %d
Error message
Invalid batch handle: statement handle %d
What it means
InvalidBatchHandleException thrown by FirebirdBatchMessageCommandExecutor when a batch MESSAGE packet references a statement handle with no open batch in FirebirdBatchRegistry for the current connection. Identical guard to the execute executor: MESSAGE rows can only be appended to an existing batch.
Source
Thrown at proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/batch/FirebirdBatchMessageCommandExecutor.java:51
import java.util.Collections;
import java.util.List;
/**
* Batch send message command executor for Firebird.
*/
@RequiredArgsConstructor
public final class FirebirdBatchMessageCommandExecutor implements CommandExecutor {
private final FirebirdBatchMessageCommandPacket packet;
private final ConnectionSession connectionSession;
@Override
public Collection<DatabasePacket> execute() throws SQLException {
int connectionId = connectionSession.getConnectionId();
FirebirdBatchStatement batchStatement = FirebirdBatchRegistry.getInstance().getBatchStatement(connectionId, packet.getStatementHandle());
if (null == batchStatement) {
throw new InvalidBatchHandleException(packet.getStatementHandle());
}
if (batchStatement.getAccumulatedSize() + packet.getDataLength() > batchStatement.getBufferSize()) {
throw new BatchTooBigException(packet.getStatementHandle(), batchStatement.getAccumulatedSize(), packet.getDataLength(), batchStatement.getBufferSize());
}
for (List<Object> each : packet.readParameterValues(batchStatement.getColumnDescriptors())) {
batchStatement.addParameterValues(each);
}
batchStatement.addSize(packet.getDataLength());
return Collections.singleton(new FirebirdGenericResponsePacket());
}
}
View on GitHub (pinned to e952770a21)
Solutions
- Wait for the CREATE BATCH generic response before streaming MESSAGE packets for that handle.
- On timeout/uncertainty, close and recreate the batch rather than continuing to send messages.
- Keep the whole batch lifecycle on one physical connection.
Defensive patterns
Strategy: validation
Validate before calling
// Wait for CREATE BATCH ack before streaming messages
if (!openBatches.contains(stmtHandle)) throw new IllegalStateException("create batch first");
sendBatchMessage(stmtHandle, data); Try / catch
catch (SQLException e) {
if (e.getMessage().contains("Invalid batch handle")) { int h = sendCreateBatch(currentPreparedHandle()); resendPendingMessages(h); } else throw e;
} Prevention
- Do not pipeline batch MESSAGE packets before the CREATE BATCH response.
- Keep the batch lifecycle on one connection.
When it happens
Trigger: FirebirdBatchMessageCommandExecutor.execute() does getBatchStatement(connectionId, packet.getStatementHandle()) and throws when null; sending batch data before CREATE BATCH succeeded, or after the batch was closed/freed.
Common situations: fire-and-forget clients that pipeline MESSAGE before reading the CREATE BATCH response; retry after a timeout where the batch was already freed; pooled connection switch between create and append.
Related errors
- Invalid batch handle: statement handle %d
- Invalid statement handle: %d
- Batch already opened for statement handle %d
- Invalid statement handle: %d
- Statement used in batch must have parameters
AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14).
Data as JSON: /api/errors/8339e4ac69040d2f.
Report an issue: GitHub.