{"record":{"id":"d7e731633953a910","repo":"pentaho/pentaho-kettle","slug":"read-beyond-last-row-rownr","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/ods/OdfSheet.java","lineNumber":116,"sourceCode":"              result -= ( (TableTableCellElement) cell ).getTableNumberColumnsRepeatedAttribute();\n            } else {\n              // get first non-empty cell from the end, break\n              break;\n            }\n          }\n        }\n      }\n    }\n    return result;\n  }\n\n  public String getName() {\n    return table.getTableName();\n  }\n\n  public KCell[] getRow( int rownr ) {\n    if ( rownr >= nrOfRows ) {\n      throw new ArrayIndexOutOfBoundsException( \"Read beyond last row: \" + rownr );\n    }\n    OdfTableRow row = table.getRowByIndex( rownr );\n    int cols = findNrColumns( row );\n    OdfCell[] xlsCells = new OdfCell[ cols ];\n    for ( int i = 0; i < cols; i++ ) {\n      OdfTableCell cell = row.getCellByIndex( i );\n      if ( cell != null ) {\n        xlsCells[i] = new OdfCell( cell );\n      }\n    }\n    return xlsCells;\n  }\n\n  public int getRows() {\n    return nrOfRows;\n  }\n\n  public KCell getCell( int colnr, int rownr ) {","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/plugins/excel/core/src/main/java/org/pentaho/di/trans/steps/excelinput/ods/OdfSheet.java#L98-L134","documentation":"OdfSheet.getRow(int rownr) throws an ArrayIndexOutOfBoundsException with the message 'Read beyond last row: <rownr>' when the requested row index is greater than or equal to the sheet's row count. It is a bounds check protecting downstream access to the ODF table, which would otherwise fail less clearly.","triggerScenarios":"Calling OdfSheet.getRow() (or the KSheet.row() wrapper that delegates to it) with a rownr >= nrOfRows, typically by iterating a fixed row count larger than the actual sheet contents.","commonSituations":"Hard-coding a row count from a stale preview; looping rows without checking sheet.getRows(); a data file truncated or regenerated with fewer rows since the transform was configured.","solutions":["Query the sheet's row count first and loop only up to it (e.g. for (int i = 0; i < sheet.getRows(); i++)).","Validate the expected row count against the actual file before processing.","Catch ArrayIndexOutOfBoundsException around row access and treat it as end-of-data if iterating speculatively.","Re-open the workbook to refresh nrOfRows if the file was modified externally."],"exampleFix":"// before\nfor (int r = 0; r < expectedRows; r++) { KCell[] row = sheet.getRow(r); }\n// after\nint rows = Math.min(expectedRows, sheet.getRows());\nfor (int r = 0; r < rows; r++) { KCell[] row = sheet.getRow(r); }","handlingStrategy":"validation","validationCode":"if (rownr >= 0 && rownr < sheet.getRows()) { KCell[] row = sheet.getRow(rownr); }","typeGuard":null,"tryCatchPattern":"try { KCell[] row = sheet.getRow(rownr); } catch (ArrayIndexOutOfBoundsException e) { row = new KCell[0]; // treat as end-of-data }","preventionTips":["Always derive loop bounds from sheet.getRows()","Remember rows are 0-indexed","Re-open the workbook if the file may shrink between config and run","Never hard-code expected row counts from stale previews"],"tags":["odf","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"}