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
- Inspect the wrapped SQLException (getCause()) for the true reason — fix the underlying connection problem first
- Ensure the Database object itself is open and connected before calling closeCallableStatement
- Use connection pooling/validation queries (setConnectionValidationQuery / validation interval) so dead connections are recycled
- Wrap cleanup in try-catch and log rather than letting the cleanup failure mask the real proc-call exception
- 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
- Always check the Database/connection is open before cleanup calls
- Enable connection validation queries with a short validation interval
- Log and swallow cleanup exceptions so they don't mask the primary error
- Keep proc-call executions short to avoid idle-connection drops
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
- Error closing connection while searching primary keys in…
- Error closing prepared statement
- Error disconnecting from database
- An error occurred executing SQL:
- An error occurred executing SQL:
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)