{"record":{"id":"6cf1631d29cd77ab","repo":"pentaho/pentaho-kettle","slug":"error-executing-foreachrow","errorCode":null,"errorMessage":"Error executing forEachRow","messagePattern":"Error executing forEachRow","errorType":"exception","errorClass":"KettleDatabaseException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/pentaho/di/core/database/Database.java","lineNumber":4125,"sourceCode":"   * @param sql      The SQL statement to execute\n   * @param limit    The maximum number of rows to process (<=0 means unlimited)\n   * @param callback The callback to execute for each row (receives Object[] row)\n   * @throws KettleDatabaseException if something goes wrong\n   */\n  public void forEachRow( String sql, int limit, java.util.function.Consumer<Object[]> callback )\n    throws KettleDatabaseException {\n    try ( ResultSet rset = openQuery( sql ) ) {\n      int count = 0;\n      while ( rset != null && ( limit <= 0 || count < limit ) ) {\n        Object[] row = getRow( rset );\n        if ( row == null ) {\n          break;\n        }\n        callback.accept( row );\n        count++;\n      }\n    } catch ( Exception e ) {\n      throw new KettleDatabaseException( \"Error executing forEachRow\", e );\n    }\n  }\n\n  public List<Object[]> getFirstRows( String tableName, int limit ) throws KettleDatabaseException {\n    return getFirstRows( tableName, limit, null );\n  }\n\n  /**\n   * Get the first rows from a table (for preview)\n   *\n   * @param tableName The table name (or schema/table combination): this needs to be quoted properly\n   * @param limit     limit <=0 means unlimited, otherwise this specifies the maximum number of rows read.\n   * @param monitor   The progress monitor to update while getting the rows.\n   * @return An ArrayList of rows.\n   * @throws KettleDatabaseException in case something goes wrong\n   */\n  public List<Object[]> getFirstRows( String tableName, int limit, ProgressMonitorListener monitor )\n    throws KettleDatabaseException {","sourceCodeStart":4107,"sourceCodeEnd":4143,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/core/src/main/java/org/pentaho/di/core/database/Database.java#L4107-L4143","documentation":"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.","triggerScenarios":"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'.","commonSituations":"Callback code assuming non-null fields, wrong row shape, or throwing application exceptions that propagate into the Kettle wrapper.","solutions":["Wrap risky callback logic internally and inspect the chained cause to see if your callback threw","Null-check row values inside the callback before use","Validate the expected column layout of the ResultSet before processing","Catch KettleDatabaseException at the call site and handle per-row failures idempotently so processing can resume"],"exampleFix":"// before\ndb.forEachRow(rs, row -> process(row[5].toString())); // NPE if null\n// after\ndb.forEachRow(rs, row -> { if (row[5] != null) process(row[5].toString()); });","handlingStrategy":"try-catch","validationCode":"// make callback total: never throw\ndb.forEachRow(rs, row -> {\n  try { process(row); } catch (Exception ex) { log.error(\"row failed: \" + Arrays.toString(row), ex); }\n});","typeGuard":"boolean isUsableRow(Object[] row) { return row != null && row.length > 0; }","tryCatchPattern":"try {\n  db.forEachRow(rs, this::processRow);\n} catch (KettleDatabaseException e) {\n  Throwable c = e.getCause();\n  if (c instanceof RuntimeException) {\n    throw (RuntimeException) c; // recover your callback's own error\n  }\n  throw e; // genuine fetch failure\n}","preventionTips":["Write callbacks that never throw — log and continue on bad rows","Null-check all accessed columns in the callback","Keep per-row processing idempotent so restarts are safe","Unwrap the cause to distinguish callback bugs from fetch failures"],"tags":["jdbc","callback","iteration"],"backgroundTag":"database-query-failed","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}