pentaho/pentaho-kettle · error · RuntimeException

Row.ErrorSerializing

Error message

Row.ErrorSerializing

What it means

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.

Solutions

  1. Clamp the start before calling: var s = Math.max(0, start); substr(str, s, len).
  2. Fix the offset computation (e.g. guard indexOf results of -1 before subtracting).
  3. Use native JavaScript substring/slice which handle negatives as offsets from the end.

Example fix

// before
substr(str, idx - 1, len); // idx can be 0 or -1
// after
var start = Math.max(0, idx - 1);
substr(str, start, len);
Defensive patterns

Strategy: validation

Validate before calling

var idx = rawIndex == null ? 0 : Math.round(Number(rawIndex) || 0);
if (idx < 0) idx = 0;

Type guard

function isNonNegativeInt(x) { return Number.isInteger(x) && x >= 0; }

Try / catch

var start = Math.max(0, computedStart);
var r = substr(str, start, len);

Prevention

When it happens

Trigger: Calling substr(str, -1, 5) or any call whose second argument rounds to a negative number (Context.toNumber then Math.round).

Common situations: 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.

Related errors


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/ee7fb288cbef8443. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/pentaho/di/compatibility/Row.java:932

      @Override
      public int compare( Row one, Row two ) {
        return one.compare( two, fieldNumbers, ascending );
      }
    };

    Collections.sort( rows, comparator );
  }

  public static final byte[] extractData( Row row ) {
    try {
      ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
      DataOutputStream dataOutputStream = new DataOutputStream( byteArrayOutputStream );
      row.writeData( dataOutputStream );
      dataOutputStream.close();
      byteArrayOutputStream.close();
      return byteArrayOutputStream.toByteArray();
    } catch ( Exception e ) {
      throw new RuntimeException( BaseMessages.getString( PKG, "Row.ErrorSerializing" ) + row, e );
    }
  }

  public static final Row getRow( byte[] data, Row metadata ) {
    try {
      ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( data );
      DataInputStream dataInputStream = new DataInputStream( byteArrayInputStream );
      return new Row( dataInputStream, metadata.size(), metadata );
    } catch ( Exception e ) {
      throw new RuntimeException( BaseMessages.getString( PKG, "Row.ErrorDeserializing" ), e );
    }
  }

  /**
   * @return the usedValueListeners
   */
  public List<ValueUsedListener> getUsedValueListeners() {
    return usedValueListeners;

View on GitHub (pinned to f3058517a1)