{"record":{"id":"152735bf5712b202","repo":"apache/shardingsphere","slug":"invalid-statement-handle-d","errorCode":null,"errorMessage":"Invalid statement handle: %d","messagePattern":"Invalid statement handle: (.+?)","errorType":"exception","errorClass":"InvalidStatementHandleException","httpStatus":null,"severity":"error","filePath":"proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/batch/FirebirdBatchCreateCommandExecutor.java","lineNumber":77,"sourceCode":"    \n    private static final int TAG_BLOB_POLICY = 4;\n    \n    // private static final int BLOB_STREAM = 3;\n    \n    private static final int WIDE_CLUMPLET_LENGTH_SIZE = 4;\n    \n    private static final int INTEGER_VALUE_LENGTH = 4;\n    \n    private final FirebirdBatchCreateCommandPacket packet;\n    \n    private final ConnectionSession connectionSession;\n    \n    @Override\n    public Collection<DatabasePacket> execute() throws SQLException {\n        int connectionId = connectionSession.getConnectionId();\n        int statementId = packet.getStatementHandle();\n        if (null == connectionSession.getServerPreparedStatementRegistry().getPreparedStatement(statementId)) {\n            throw new InvalidStatementHandleException(statementId);\n        }\n        if (null != FirebirdBatchRegistry.getInstance().getBatchStatement(connectionId, statementId)) {\n            throw new BatchAlreadyOpenedException(statementId);\n        }\n        ByteBuf batchBlr = packet.getBatchBlr();\n        int blrLength = batchBlr.readableBytes();\n        FirebirdParseBatchBlr messageFormat = FirebirdParseBatchBlr.parse(batchBlr, blrLength);\n        if (messageFormat.getFields().isEmpty()) {\n            throw new BatchParametersRequiredException(statementId);\n        }\n        if (packet.getBatchMessageLength() != messageFormat.getMessageLength()) {\n            throw new InvalidBatchMessageFormatException(\n                    String.format(\"invalid message length: computed %d from BLR but client sent %d\", messageFormat.getMessageLength(), packet.getBatchMessageLength()));\n        }\n        ByteBuf batchParametersBuffer = packet.getBatchParametersBuffer();\n        BatchParameters batchParameters = BatchParameters.parse(batchParametersBuffer);\n        FirebirdBatchRegistry.getInstance().registerBatchStatement(connectionId, statementId,\n                new FirebirdBatchStatement(statementId, messageFormat.getFields(), batchParameters.getBufferSize(), batchParameters.isRecordCounts(), batchParameters.isMultiError()));","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/apache/shardingsphere/blob/e952770a215630a3659c75d64369168cd3e26b82/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/batch/FirebirdBatchCreateCommandExecutor.java#L59-L95","documentation":"InvalidStatementHandleException thrown by FirebirdBatchCreateCommandExecutor when the client asks to create a batch for a statement handle that is not in the connection's ServerPreparedStatementRegistry. It mirrors Firebird's native 'Invalid statement handle' error: you cannot batch-execute a statement that was never prepared (or was already freed) on this connection.","triggerScenarios":"FirebirdBatchCreateCommandExecutor.execute() reads packet.getStatementHandle() and looks up connectionSession.getServerPreparedStatementRegistry().getPreparedStatement(statementId); a null result (never prepared, already dropped/unprepared via FREE STATEMENT, or handle from a different connection) triggers the throw.","commonSituations":"client sends CREATE BATCH before PREPARE; handle reuse after the statement was freed; statement handle created on another connection in a connection pool that reuses handles across physical sessions; client bug mis-parsing the handle returned by the prepare response.","solutions":["Always run PREPARE first and use the statement handle returned in the prepare response for the subsequent CREATE BATCH on the same connection.","Do not send CREATE BATCH after FREE STATEMENT (option DROP/UNPREPARE) for that handle; re-prepare instead.","Ensure pooled clients do not carry statement handles across physical Firebird proxy connections.","If writing a raw-protocol client, verify you parse the statement handle from the correct response field (int4 in the generic response data)."],"exampleFix":"// before: batch created with a stale/foreign handle\nstmtHandle = 0; // never prepared\nsendCreateBatch(stmtHandle);\n\n// after: prepare first, then batch\nprepResp = sendPrepare(sql);\nsendCreateBatch(prepResp.statementHandle);","handlingStrategy":"validation","validationCode":"// Client: only batch handles you actually prepared on this connection\nSet<Integer> prepared = new HashSet<>();\nprepared.add(sendPrepare(sql).statementHandle);\nif (!prepared.contains(stmtHandle)) throw new IllegalStateException(\"handle not prepared\");","typeGuard":null,"tryCatchPattern":"catch (SQLException e) {\n    if (e.getMessage().contains(\"Invalid statement handle\")) { int h = sendPrepare(sql).statementHandle; sendCreateBatch(h); }\n    else throw e;\n}","preventionTips":["Track prepared statement handles per connection.","Re-prepare instead of reusing handles after FREE."],"tags":["firebird","prepared-statement","batch","statement-handle"],"backgroundTag":null,"analyzedSha":"e952770a215630a3659c75d64369168cd3e26b82","analyzedAt":"2026-08-14T13:54:53.392Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}