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

  1. Read the wrapped cause (ex.getCause()) for the real failure; verify the target table exists and the connection user has INSERT/TRUNCATE rights
  2. Run logBasic's printed 'Launching command: ...' COPY statement directly in psql to reproduce and fix the SQL
  3. Confirm the database connection uses the PostgreSQL JDBC driver so getConnection() is a PGConnection
  4. 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

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


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)