pentaho/pentaho-kettle · error · KettleDatabaseException

Unable to close prepared statement pstmt

Error message

Unable to close prepared statement pstmt

What it means

Thrown by Database.getOneRow( String sql ) after successfully fetching a row, when closing the shared prepared statement member pstmt fails. Kettle wraps the driver's exception in a KettleDatabaseException with this message. Like the resultset-close failure, it indicates the JDBC connection or driver is in a bad state, and it happens after the data was read — so it is a resource-release failure, not a query failure.

Solutions

  1. Inspect the exception cause for the underlying SQLException and address the connection state issue
  2. Reconnect the Database (disconnect/connect) before further use; consider closing and recreating the Database object entirely
  3. Upgrade the JDBC driver if close() spuriously throws
  4. Do not share the Database instance across threads or interleave other operations that would close pstmt while getOneRow runs

Example fix

// before
RowMetaAndData r = database.getOneRow( sql ); // throws "Unable to close prepared statement pstmt"
// after
RowMetaAndData r;
try {
  r = database.getOneRow( sql );
} catch ( KettleDatabaseException e ) {
  if ( e.getCause() != null ) log.logError( "pstmt close failed", e.getCause() );
  database.disconnect();
  database.connect();
  r = database.getOneRow( sql );
}
Defensive patterns

Strategy: try-catch

Validate before calling

if ( database.getConnection() == null ) {
  database.connect(); // ensure a live connection so pstmt close will succeed
}

Type guard

boolean hasLiveConnection( Database db ) {
  try {
    return db != null && db.getConnection() != null && !db.getConnection().isClosed();
  } catch ( SQLException e ) {
    return false;
  }
}

Try / catch

try {
  RowMetaAndData row = database.getOneRow( sql );
} catch ( KettleDatabaseException e ) {
  if ( e.getMessage() != null && e.getMessage().contains( "pstmt" ) ) {
    log.logError( "prepared statement cleanup failed", e.getCause() );
    database.disconnect();
    database.connect();
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling db.getOneRow( sql ) where the underlying query used a PreparedStatement stored in the pstmt field (openQuery with parameters), and pstmt.close() threw — typically because the connection was already closed, aborted, or the driver rejected the close call.

Common situations: Connection killed by the server between fetch and cleanup (idle timeouts, failover); shared Database instance whose pstmt was closed/nulled by another code path mid-flight; driver bugs with prepared-statement deallocation; running inside a transaction that was rolled back by another thread.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/c4edf0b617a9f99d. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/pentaho/di/core/database/Database.java:3619

   * @param sql The SQL for the query
   * @return one Row with data or null if nothing was found.
   */
  public RowMetaAndData getOneRow( String sql ) throws KettleDatabaseException {
    ResultSet rs = openQuery( sql );
    if ( rs != null ) {
      Object[] row = getRow( rs ); // One row only

      try {
        rs.close();
      } catch ( Exception e ) {
        throw new KettleDatabaseException( "Unable to close resultset", e );
      }

      if ( pstmt != null ) {
        try {
          pstmt.close();
        } catch ( Exception e ) {
          throw new KettleDatabaseException( "Unable to close prepared statement pstmt", e );
        }
        pstmt = null;
      }
      if ( selStmt != null ) {
        try {
          selStmt.close();
        } catch ( Exception e ) {
          throw new KettleDatabaseException( "Unable to close prepared statement sel_stmt", e );
        }
        selStmt = null;
      }
      return new RowMetaAndData( rowMeta, row );
    } else {
      throw new KettleDatabaseException( "error opening resultset for query: " + sql );
    }
  }

  public RowMeta getMetaFromRow( Object[] row, ResultSetMetaData md ) throws SQLException, KettleDatabaseException {

View on GitHub (pinned to f3058517a1)