{"record":{"id":"4fe017d059e12a68","repo":"pentaho/pentaho-kettle","slug":"start-smaller-than-0","errorCode":null,"errorMessage":"start smaller than 0","messagePattern":"start smaller than 0","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"engine/src/main/java/org/pentaho/di/trans/steps/script/ScriptAddedFunctions.java","lineNumber":1629,"sourceCode":"      } 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        }\n        if ( len < 0 ) {\n          len = 0; // Make it compatible with Javascript substr\n        }\n\n        to = from + len;\n        strLen = sRC.length();\n        if ( to > strLen ) {\n          to = strLen;\n        }\n        sRC = sRC.substring( from, to );\n      } catch ( Exception e ) {\n        throw new RuntimeException( \"The function call substr is not valid : \" + e.getMessage() );\n      }\n    } else {\n      throw new RuntimeException( \"The function call substr requires 2 or 3 arguments.\" );\n    }\n    return sRC;","sourceCodeStart":1611,"sourceCodeEnd":1647,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/engine/src/main/java/org/pentaho/di/trans/steps/script/ScriptAddedFunctions.java#L1611-L1647","documentation":"Explicit validation inside the 3-argument substr() helper: the start index (ArgList[1], rounded to int) must not be negative. Unlike real JavaScript substr(), which treats negative starts as offsets from the end, this Java-backed implementation rejects them.","triggerScenarios":"Calling substr(str, -N, len) or substr(str, someNegativeVar, len); notably porting JavaScript substr code that relied on negative start-from-end semantics.","commonSituations":"Migrating browser/Rhino-native substr logic into the Kettle JavaScript step; computing the start index with arithmetic that can go below zero (e.g. indexOf returning -1).","solutions":["Clamp the start: if (start < 0) start = 0; before calling substr.","Compute indexOf results defensively: var idx = str.indexOf('x'); var start = idx < 0 ? 0 : idx.","To emulate negative-from-end, translate yourself: substr(str, str.length + negStart, len) when str.length + negStart >= 0.","Clamp the translated start to 0 as well, since length+neg can still be negative."],"exampleFix":"// before\nvar s = substr(str, -3, 3);\n// after\nvar start = str.length - 3; if (start < 0) start = 0; var s = substr(str, start, 3);","handlingStrategy":"validation","validationCode":"var start = Number(fromExpr); if (!(start >= 0)) start = 0; var s = substr(str, start, len);","typeGuard":"function hasValidStart(start){ return typeof start === 'number' && isFinite(start) && start >= 0; }","tryCatchPattern":"try { s = substr(str, start, len); } catch (e) { if (/start smaller than 0/.test(e.message)) s = ''; else throw e; }","preventionTips":["Never pass negative starts — this substr does not support from-end semantics.","Clamp indexOf() results (-1) before using as start.","Translate negative JavaScript-substr starts yourself: str.length + neg."],"tags":["javascript","substr","negative-index","argument-out-of-range","script-step"],"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"}