{"record":{"id":"27b2d9c989e576f4","repo":"pentaho/pentaho-kettle","slug":"the-function-call-substr-is-not-valid","errorCode":null,"errorMessage":"The function call substr is not valid : ","messagePattern":"The function call substr is not valid : ","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"engine/src/main/java/org/pentaho/di/trans/steps/script/ScriptAddedFunctions.java","lineNumber":1612,"sourceCode":"    return sRC;\n  }\n\n  public static String substr( ScriptEngine actualContext, Bindings actualObject, Object[] ArgList,\n    Object FunctionContext ) {\n    String sRC = \"\";\n\n    if ( ArgList.length == 2 ) {\n      try {\n        if ( isNull( ArgList[0] ) ) {\n          return null;\n        } else if ( isUndefined( ArgList[0] ) ) {\n          return (String) undefinedValue;\n        }\n        sRC = (String) ArgList[0];\n        int from = (int) Math.round( (Double) ArgList[1] );\n        sRC = sRC.substring( from );\n      } catch ( Exception e ) {\n        throw new RuntimeException( \"The function call substr is not valid : \" + e.getMessage() );\n      }\n    } else if ( ArgList.length == 3 ) {\n      try {\n        int to;\n        int strLen;\n\n        if ( isNull( ArgList[0] ) ) {\n          return null;\n        } else if ( isUndefined( ArgList[0] ) ) {\n          return (String) undefinedValue;\n        }\n        sRC = (String) ArgList[0];\n        int from = (int) Math.round( (Double) ArgList[1] );\n        int len = (int) Math.round( (Double) ArgList[2] );\n\n        if ( from < 0 ) {\n          throw new RuntimeException( \"start smaller than 0\" );\n        }","sourceCodeStart":1594,"sourceCodeEnd":1630,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/engine/src/main/java/org/pentaho/di/trans/steps/script/ScriptAddedFunctions.java#L1594-L1630","documentation":"Thrown when the 2-argument form of substr() throws while slicing the string. The 2-arg form casts ArgList[0] to String and ArgList[1] to a Double that is rounded to an int start index; any failure (bad string, non-numeric start, index out of range) is wrapped in this message with the cause appended.","triggerScenarios":"substr(str, start) where str is not a string, start is not a number (e.g. a string '2' or null), or start is out of the string's valid index range so String.substring throws StringIndexOutOfBoundsException.","commonSituations":"Passing a text field as the start index; forgetting that JavaScript numbers are fine but Java-side casting requires numeric values; start index beyond string length.","solutions":["Ensure the start argument is a number: substr(str, parseInt(start, 10)).","Validate 0 <= start < str.length before calling: if (start >= 0 && start < str.length) substr(...).","Coerce the string argument: substr('' + str, start).","Read the cause appended after the colon to pinpoint which argument failed."],"exampleFix":"// before\nvar s = substr(myField, myIndex); // myIndex is a string\n// after\nvar s = substr('' + myField, parseInt(myIndex, 10));","handlingStrategy":"validation","validationCode":"function safeSubstr2(s, start){ s = '' + s; start = Number(start); if (!isFinite(start) || start < 0) start = 0; if (start > s.length) start = s.length; return s.substring(start); }","typeGuard":"function canSubstr2(s, start){ return typeof s === 'string' && typeof start === 'number' && start >= 0 && start <= s.length; }","tryCatchPattern":"try { s = substr(str, start); } catch (e) { s = ''; logError('substr failed: ' + e.message); }","preventionTips":["Validate start is a finite number in [0, str.length].","Coerce row fields to string/number explicitly.","Use the safeSubstr wrapper instead of raw substr."],"tags":["javascript","substr","index-out-of-range","script-step"],"backgroundTag":"index-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"}