pentaho/pentaho-kettle · error · KettleException
Error while preparing the COPY
Error message
Error while preparing the COPY
What it means
do_copy wraps the setup of the PostgreSQL COPY operation — truncation and creation of the PGCopyOutputStream — in a try/catch and rethrows any failure as a KettleException prefixed with 'Error while preparing the COPY ' plus the command. It indicates the COPY could not even be started (connection or SQL problem), not that row data was bad.
Solutions
- Read the wrapped cause (ex.getCause()) for the real failure; verify the target table exists and the connection user has INSERT/TRUNCATE rights
- Run logBasic's printed 'Launching command: ...' COPY statement directly in psql to reproduce and fix the SQL
- Confirm the database connection uses the PostgreSQL JDBC driver so getConnection() is a PGConnection
- Fix the truncate step separately (see 'Error while truncating') if the cause comes from processTruncate
Example fix
// before
pgCopyOut = new PGCopyOutputStream( (PGConnection) data.db.getConnection(), copyCmd );
// after (validate connection type first to get a clearer error)
Connection conn = data.db.getConnection();
if ( !( conn instanceof PGConnection ) ) {
throw new KettleException( "Connection is not a PostgreSQL connection" );
}
pgCopyOut = new PGCopyOutputStream( (PGConnection) conn, copyCmd ); Defensive patterns
Strategy: try-catch
Validate before calling
// before running the transformation
if ( db.connectAndCheck( meta.getDatabaseMeta() ) == null ) {
throw new IllegalStateException( "Cannot connect to PostgreSQL before bulk load" );
}
DatabaseMeta dm = meta.getDatabaseMeta();
if ( dm == null || !dm.getDatabaseTypeDesc().toLowerCase().contains( "postgres" ) ) {
throw new IllegalStateException( "PGBulkLoader requires a PostgreSQL connection" );
} Type guard
boolean isPgConnection( Connection c ) {
return c instanceof org.postgresql.PGConnection;
} Try / catch
try {
transformation.startExecution();
} catch ( KettleException e ) {
Throwable cause = e.getCause();
log.error( "COPY setup failed: " + e.getMessage(), cause ); // cause holds the real SQL/Cast error
} Prevention
- Test the database connection button in the step dialog before running
- Ensure the target table exists and the user has INSERT and TRUNCATE privileges
- Always use the PostgreSQL JDBC driver for this step
- Execute the logged COPY command manually in psql to validate it
When it happens
Trigger: Any exception in the try block of do_copy: processTruncate() throwing (e.g. TRUNCATE fails), data.db.getConnection() failing or returning a non-PGConnection object (ClassCastException), or the PGCopyOutputStream constructor rejecting copyCmd (bad COPY SQL, missing table, insufficient privileges).
Common situations: Target table doesn't exist or was renamed; DB user lacks TRUNCATE privilege; connection dropped between init and first row; copyCmd malformed due to bad table/field names needing quoting; JDBC connection is not a PGConnection (wrong driver for the database).
Understand the failure class
Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.
Related errors
- An error occurred executing SQL:
- An error occurred executing SQL:
- Couldn't execute SQL:
- Couldn't find any rows because of an error :
- Couldn't get a result because of an error :
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/abd0fba6e495803f.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/postgresql-db-bulk-loader/impl/src/main/java/org/pentaho/di/trans/steps/pgbulkloader/PGBulkLoader.java:170
statement.close();
}
}
private void do_copy( PGBulkLoaderMeta meta, boolean wait ) throws KettleException {
data.db = getDatabase( this, meta );
String copyCmd = getCopyCommand();
try {
connect();
checkClientEncoding();
processTruncate();
logBasic( "Launching command: " + copyCmd );
pgCopyOut = new PGCopyOutputStream( (PGConnection) data.db.getConnection(), copyCmd );
} catch ( Exception ex ) {
throw new KettleException( "Error while preparing the COPY " + copyCmd, ex );
}
}
@VisibleForTesting
Database getDatabase( LoggingObjectInterface parentObject, PGBulkLoaderMeta pgBulkLoaderMeta ) {
DatabaseMeta dbMeta = pgBulkLoaderMeta.getDatabaseMeta();
// If dbNameOverride is present, clone the origin db meta and override the DB name
String dbNameOverride = environmentSubstitute( pgBulkLoaderMeta.getDbNameOverride() );
if ( !Utils.isEmpty( dbNameOverride ) ) {
dbMeta = (DatabaseMeta) pgBulkLoaderMeta.getDatabaseMeta().clone();
dbMeta.setDBName( dbNameOverride.trim() );
logDebug( "DB name overridden to the value: " + dbNameOverride );
}
return new Database( parentObject, dbMeta );
}
void connect() throws KettleException {
if ( getTransMeta().isUsingUniqueConnections() ) {View on GitHub (pinned to f3058517a1)