{"record":{"id":"ee7fb288cbef8443","repo":"pentaho/pentaho-kettle","slug":"row-errorserializing","errorCode":null,"errorMessage":"Row.ErrorSerializing","messagePattern":"Row\\.ErrorSerializing","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/pentaho/di/compatibility/Row.java","lineNumber":932,"sourceCode":"      @Override\n      public int compare( Row one, Row two ) {\n        return one.compare( two, fieldNumbers, ascending );\n      }\n    };\n\n    Collections.sort( rows, comparator );\n  }\n\n  public static final byte[] extractData( Row row ) {\n    try {\n      ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();\n      DataOutputStream dataOutputStream = new DataOutputStream( byteArrayOutputStream );\n      row.writeData( dataOutputStream );\n      dataOutputStream.close();\n      byteArrayOutputStream.close();\n      return byteArrayOutputStream.toByteArray();\n    } catch ( Exception e ) {\n      throw new RuntimeException( BaseMessages.getString( PKG, \"Row.ErrorSerializing\" ) + row, e );\n    }\n  }\n\n  public static final Row getRow( byte[] data, Row metadata ) {\n    try {\n      ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( data );\n      DataInputStream dataInputStream = new DataInputStream( byteArrayInputStream );\n      return new Row( dataInputStream, metadata.size(), metadata );\n    } catch ( Exception e ) {\n      throw new RuntimeException( BaseMessages.getString( PKG, \"Row.ErrorDeserializing\" ), e );\n    }\n  }\n\n  /**\n   * @return the usedValueListeners\n   */\n  public List<ValueUsedListener> getUsedValueListeners() {\n    return usedValueListeners;","sourceCodeStart":914,"sourceCodeEnd":950,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/core/src/main/java/org/pentaho/di/compatibility/Row.java#L914-L950","documentation":"In the 3-argument form of substr(str, start, len), the function explicitly rejects a negative start index with 'start smaller than 0'. This guard exists because the implementation computes to = from + len and calls sRC.substring(from, to), which would throw StringIndexOutOfBoundsException for negative from; the dedicated message makes the cause clear to script authors.","triggerScenarios":"Calling substr(str, -1, 5) or any call whose second argument rounds to a negative number (Context.toNumber then Math.round).","commonSituations":"Porting scripts that used native JavaScript substr, which treats negative start as 0; computing start as (indexOf(...) - 1) when the needle is at position 0 or absent (-1); row data with negative offsets.","solutions":["Clamp the start before calling: var s = Math.max(0, start); substr(str, s, len).","Fix the offset computation (e.g. guard indexOf results of -1 before subtracting).","Use native JavaScript substring/slice which handle negatives as offsets from the end."],"exampleFix":"// before\nsubstr(str, idx - 1, len); // idx can be 0 or -1\n// after\nvar start = Math.max(0, idx - 1);\nsubstr(str, start, len);","handlingStrategy":"validation","validationCode":"var idx = rawIndex == null ? 0 : Math.round(Number(rawIndex) || 0);\nif (idx < 0) idx = 0;","typeGuard":"function isNonNegativeInt(x) { return Number.isInteger(x) && x >= 0; }","tryCatchPattern":"var start = Math.max(0, computedStart);\nvar r = substr(str, start, len);","preventionTips":["Clamp computed offsets (especially indexOf - 1) to >= 0","Remember this differs from native JS substr, which allows negative start","Guard indexOf() results of -1 before doing arithmetic"],"tags":["javascript","string-processing","argument-out-of-range","scripting"],"backgroundTag":"argument-out-of-range","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"}