{"record":{"id":"4ba0408b9fe98a66","repo":"pentaho/pentaho-kettle","slug":"read-beyond-last-row-rownr-poisheet","errorCode":null,"errorMessage":"Read beyond last row: \" + rownr","messagePattern":"Read beyond last row: \" \\+ rownr","errorType":"exception","errorClass":"ArrayIndexOutOfBoundsException","httpStatus":null,"severity":"error","filePath":"plugins/excel/core/src/main/java/org/pentaho/di/trans/steps/excelinput/poi/PoiSheet.java","lineNumber":38,"sourceCode":"import org.pentaho.di.core.spreadsheet.KCell;\nimport org.pentaho.di.core.spreadsheet.KSheet;\n\npublic class PoiSheet implements KSheet {\n  private Sheet sheet;\n\n  public PoiSheet( Sheet sheet ) {\n    this.sheet = sheet;\n  }\n\n  public String getName() {\n    return sheet.getSheetName();\n  }\n\n  public KCell[] getRow( int rownr ) {\n    if ( rownr < sheet.getFirstRowNum() ) {\n      return new KCell[] {};\n    } else if ( rownr > sheet.getLastRowNum() ) {\n      throw new ArrayIndexOutOfBoundsException( \"Read beyond last row: \" + rownr );\n    }\n    Row row = sheet.getRow( rownr );\n    if ( row == null ) { // read an empty row\n      return new KCell[] {};\n    }\n    int cols = row.getLastCellNum();\n    if ( cols < 0 ) { // this happens if a row has no cells, POI returns -1 then\n      return new KCell[] {};\n    }\n    PoiCell[] xlsCells = new PoiCell[cols];\n    for ( int i = 0; i < cols; i++ ) {\n      Cell cell = row.getCell( i );\n      if ( cell != null ) {\n        xlsCells[i] = new PoiCell( cell );\n      }\n    }\n    return xlsCells;\n  }","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/plugins/excel/core/src/main/java/org/pentaho/di/trans/steps/excelinput/poi/PoiSheet.java#L20-L56","documentation":"PoiSheet.getRow(int rownr) throws ArrayIndexOutOfBoundsException ('Read beyond last row: <rownr>') when rownr exceeds sheet.getLastRowNum(). Note the asymmetry: a rownr below the first row number returns an empty array rather than throwing, only indices past the last row are rejected.","triggerScenarios":"Requesting a row index greater than the POI sheet's last row number — usually from a loop bounded by a configured or hard-coded row count rather than the sheet's real extent.","commonSituations":"Looping to a fixed end row after the source file shrank; using a 1-based row count where the API expects 0-based indices; previewing a sheet whose data changed between configuration and run.","solutions":["Bound loops with sheet.getRows()/getLastRowNum() instead of a hard-coded count.","Verify your row indices are 0-based; POI and this API both index from 0.","Catch ArrayIndexOutOfBoundsException as end-of-data when doing speculative reads.","Re-read the workbook if the file may have been modified since opening."],"exampleFix":"// before\nfor (int r = 0; r <= 5000; r++) sheet.getRow(r); // may exceed last row\n// after\nint last = sheet.getRows();\nfor (int r = 0; r < last; r++) sheet.getRow(r);","handlingStrategy":"validation","validationCode":"if (rownr >= sheet.getFirstRowNum() && rownr <= sheet.getLastRowNum()) { KCell[] row = sheet.getRow(rownr); }","typeGuard":null,"tryCatchPattern":"try { KCell[] row = sheet.getRow(rownr); } catch (ArrayIndexOutOfBoundsException e) { row = new KCell[]{}; }","preventionTips":["Bound iteration by getLastRowNum()/getRows(), not constants","Use 0-based indices consistently","Treat rows before firstRowNum as empty arrays (API returns them anyway)","Refresh the workbook reference if the file changed on disk"],"tags":["poi","spreadsheet","array-index-out-of-bounds","row-access"],"backgroundTag":"index-out-of-bounds","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"}