pentaho/pentaho-kettle · error · KettleDatabaseException

Error executing forEachRow

Error message

Error executing forEachRow

What it means

Database.forEachRow(...) executes a callback for each row of a ResultSet; any exception thrown inside the loop — including from the user-supplied callback itself — is wrapped in this KettleDatabaseException. It can therefore indicate either a fetch failure or a bug in your callback code.

Solutions

  1. Wrap risky callback logic internally and inspect the chained cause to see if your callback threw
  2. Null-check row values inside the callback before use
  3. Validate the expected column layout of the ResultSet before processing
  4. Catch KettleDatabaseException at the call site and handle per-row failures idempotently so processing can resume

Example fix

// before
db.forEachRow(rs, row -> process(row[5].toString())); // NPE if null
// after
db.forEachRow(rs, row -> { if (row[5] != null) process(row[5].toString()); });
Defensive patterns

Strategy: try-catch

Validate before calling

// make callback total: never throw
db.forEachRow(rs, row -> {
  try { process(row); } catch (Exception ex) { log.error("row failed: " + Arrays.toString(row), ex); }
});

Type guard

boolean isUsableRow(Object[] row) { return row != null && row.length > 0; }

Try / catch

try {
  db.forEachRow(rs, this::processRow);
} catch (KettleDatabaseException e) {
  Throwable c = e.getCause();
  if (c instanceof RuntimeException) {
    throw (RuntimeException) c; // recover your callback's own error
  }
  throw e; // genuine fetch failure
}

Prevention

When it happens

Trigger: Calling db.forEachRow(rs or sql, callback) where callback.accept(row) throws (NPE, business-logic error) or the underlying row fetch fails; the catch wraps it as 'Error executing forEachRow'.

Common situations: Callback code assuming non-null fields, wrong row shape, or throwing application exceptions that propagate into the Kettle wrapper.

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

Appendix: source

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

   * @param sql      The SQL statement to execute
   * @param limit    The maximum number of rows to process (<=0 means unlimited)
   * @param callback The callback to execute for each row (receives Object[] row)
   * @throws KettleDatabaseException if something goes wrong
   */
  public void forEachRow( String sql, int limit, java.util.function.Consumer<Object[]> callback )
    throws KettleDatabaseException {
    try ( ResultSet rset = openQuery( sql ) ) {
      int count = 0;
      while ( rset != null && ( limit <= 0 || count < limit ) ) {
        Object[] row = getRow( rset );
        if ( row == null ) {
          break;
        }
        callback.accept( row );
        count++;
      }
    } catch ( Exception e ) {
      throw new KettleDatabaseException( "Error executing forEachRow", e );
    }
  }

  public List<Object[]> getFirstRows( String tableName, int limit ) throws KettleDatabaseException {
    return getFirstRows( tableName, limit, null );
  }

  /**
   * Get the first rows from a table (for preview)
   *
   * @param tableName The table name (or schema/table combination): this needs to be quoted properly
   * @param limit     limit <=0 means unlimited, otherwise this specifies the maximum number of rows read.
   * @param monitor   The progress monitor to update while getting the rows.
   * @return An ArrayList of rows.
   * @throws KettleDatabaseException in case something goes wrong
   */
  public List<Object[]> getFirstRows( String tableName, int limit, ProgressMonitorListener monitor )
    throws KettleDatabaseException {

View on GitHub (pinned to f3058517a1)