apache/shardingsphere · error · InvalidStatementHandleException

Invalid statement handle: %d

Error message

Invalid statement handle: %d

What it means

InvalidStatementHandleException thrown by FirebirdExecuteStatementCommandExecutor when the statement handle in an EXECUTE statement packet is absent from the connection's ServerPreparedStatementRegistry. The proxy refuses to execute what was never prepared or was already freed on this session.

Source

Thrown at proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/command/query/statement/execute/FirebirdExecuteStatementCommandExecutor.java:83

@RequiredArgsConstructor
public final class FirebirdExecuteStatementCommandExecutor implements CommandExecutor {
    
    private final FirebirdExecuteStatementPacket packet;
    
    private final ConnectionSession connectionSession;
    
    private ProxyBackendHandler proxyBackendHandler;
    
    @Getter
    private ResponseType responseType;
    
    @Override
    public Collection<DatabasePacket> execute() throws SQLException {
        connectionSession.beginPreparedStatementCache(FirebirdStatementResourceCleaner.createPreparedStatementCacheKey(packet.getStatementId()));
        try {
            FirebirdServerPreparedStatement preparedStatement = connectionSession.getServerPreparedStatementRegistry().getPreparedStatement(packet.getStatementId());
            if (null == preparedStatement) {
                throw new InvalidStatementHandleException(packet.getStatementId());
            }
            validateTransactionHandle();
            ResponseHeader responseHeader = executePreparedStatement(preparedStatement, packet.getParameterValues());
            if (responseHeader instanceof QueryResponseHeader) {
                responseType = ResponseType.QUERY;
                FirebirdFetchStatementCache.getInstance().registerStatement(connectionSession.getConnectionId(), packet.getStatementId(), proxyBackendHandler);
                connectionSession.getDatabaseConnectionManager().markResourceInUse(proxyBackendHandler);
            } else {
                responseType = ResponseType.UPDATE;
                preparedStatement.setAffectedRows(((UpdateResponseHeader) responseHeader).getUpdateCount());
            }
            Collection<DatabasePacket> result = new LinkedList<>();
            if (packet.isStoredProcedure() && proxyBackendHandler.next()) {
                result.add(getSQLResponse());
            }
            result.add(new FirebirdGenericResponsePacket());
            return result;
        } finally {

View on GitHub (pinned to e952770a21)

Solutions

  1. Always PREPARE on the same connection before EXECUTE and take the handle from the prepare response.
  2. After FREE STATEMENT or session-level cleanup, re-prepare instead of reusing old handles.
  3. Never share statement handles across connections from a pool.
  4. Check for races where one thread frees statements while another executes them.
Defensive patterns

Strategy: validation

Validate before calling

Integer handle = preparedHandles.get(sql);
if (handle == null) { handle = sendPrepare(sql).statementHandle; preparedHandles.put(sql, handle); }
sendExecuteStatement(handle, params);

Try / catch

catch (SQLException e) {
    if (e.getMessage().contains("Invalid statement handle")) { int h = sendPrepare(sql).statementHandle; sendExecuteStatement(h, params); } else throw e;
}

Prevention

When it happens

Trigger: execute() begins a prepared-statement cache scope, then getPreparedStatement(packet.getStatementId()) returns null; happens when EXECUTE precedes PREPARE, follows FREE STATEMENT (DROP/UNPREPARE), or uses a handle from a different connection.

Common situations: client (or ORM layer) executes a statement id it cached across a reconnect; FREE sent by a cleanup thread races with a pending EXECUTE; raw-protocol client misreads the handle from the prepare response; connection pool handle leakage between sessions.

Related errors


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