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

  1. Wait for the CREATE BATCH generic response before streaming MESSAGE packets for that handle.
  2. On timeout/uncertainty, close and recreate the batch rather than continuing to send messages.
  3. 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

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


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