pentaho/pentaho-kettle · warning · KettleDatabaseException

Database.Exception.ErrorClosingCallableStatement

Error message

Database.Exception.ErrorClosingCallableStatement

What it means

Thrown by Database.closeCallableStatement() (Database.java:4952) when ResultSet.freeing the CallableStatement via close() fails with a SQLException during cleanup of a stored-procedure call. It wraps the underlying SQLException with the message 'Error closing Callable Statement'. Usually a symptom of an already-broken connection rather than the primary problem.

Solutions

  1. Inspect the wrapped SQLException (getCause()) for the true reason — fix the underlying connection problem first
  2. Ensure the Database object itself is open and connected before calling closeCallableStatement
  3. Use connection pooling/validation queries (setConnectionValidationQuery / validation interval) so dead connections are recycled
  4. Wrap cleanup in try-catch and log rather than letting the cleanup failure mask the real proc-call exception
  5. Upgrade JDBC driver if the driver throws spuriously on close of completed statements

Example fix

// before
cstmt.close();
// after
try {
  if ( cstmt != null ) {
    cstmt.close();
  }
} catch ( SQLException cleanupEx ) {
  log.logMinimal( "Ignored error closing callable statement", cleanupEx ); // don't mask primary exception
}
Defensive patterns

Strategy: try-catch

Validate before calling

if ( database != null && database.getConnection() != null && !database.getConnection().isClosed() ) {
  database.closeCallableStatement();
}

Type guard

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

Try / catch

try {
  database.closeCallableStatement();
} catch ( KettleDatabaseException e ) {
  SQLException cause = (SQLException) e.getCause();
  log.warn( "Callable statement cleanup failed: " + cause.getMessage(), cause );
}

Prevention

When it happens

Trigger: Calling Database.closeCallableStatement() (or Database.closeProcedureStatement()) after executing a stored procedure call, when the JDBC CallableStatement.close() throws SQLException — e.g. the connection was already closed, dropped by the DB, or the statement is in an inconsistent state after a failed execution.

Common situations: Long-running transformations where the DB or firewall drops idle connections between proc calls; calling closeCallableStatement on an already-closed Database; stored procedure execution errors leaving the statement in a bad state; connection-pool evictions in clustered environments.

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/df06aea2620ff81c. Report an issue: GitHub.

Appendix: source

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

        }
      } while ( moreResults || ( updateCount > -1 ) );

      return ret;
    } catch ( Exception ex ) {
      throw new KettleDatabaseException( "Unable to call procedure", ex );
    }
  }

  public void closeProcedureStatement() throws KettleDatabaseException {
    // CHE: close the callable statement involved in the stored
    // procedure call!
    try {
      if ( cstmt != null ) {
        cstmt.close();
        cstmt = null;
      }
    } catch ( SQLException ex ) {
      throw new KettleDatabaseException( BaseMessages.getString(
        PKG, "Database.Exception.ErrorClosingCallableStatement" ), ex );
    }
  }

  /**
   * Return SQL CREATION statement for a Table
   *
   * @param tableName The table to create
   * @throws KettleDatabaseException
   */

  public String getDDLCreationTable( String tableName, RowMetaInterface fields ) throws KettleDatabaseException {

    // First, check for reserved SQL in the input row r...
    databaseMeta.quoteReservedWords( fields );
    String quotedTk = databaseMeta.quoteField( null );

    return getCreateTableStatement( tableName, fields, quotedTk, false, null, true );

View on GitHub (pinned to f3058517a1)