{"record":{"id":"efb19c621b306751","repo":"apple/pkl","slug":"charindexoutofrange-efb19c","errorCode":"charIndexOutOfRange","errorMessage":"charIndexOutOfRange","messagePattern":"charIndexOutOfRange","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkl-core/src/main/java/org/pkl/core/stdlib/base/StringNodes.java","lineNumber":241,"sourceCode":"      var charIndex = VmUtils.codePointOffsetToCharOffset(self, index);\n      if (charIndex == -1 || charIndex == self.length()) return VmNull.withoutDefault();\n\n      if (Character.isHighSurrogate(self.charAt(charIndex))\n          && charIndex < self.length() - 1\n          && Character.isLowSurrogate(self.charAt(charIndex + 1))) {\n        return self.substring(charIndex, charIndex + 2);\n      }\n      return self.substring(charIndex, charIndex + 1);\n    }\n  }\n\n  public abstract static class substring extends ExternalMethod2Node {\n    @TruffleBoundary\n    @Specialization\n    protected String eval(String self, long start, long exclusiveEnd) {\n      var charStart = VmUtils.codePointOffsetToCharOffset(self, start);\n      if (charStart == -1) {\n        throw exceptionBuilder()\n            .evalError(\"charIndexOutOfRange\", start, 0, self.codePointCount(0, self.length()))\n            .withProgramValue(\"String\", self)\n            .build();\n      }\n\n      var charExclusiveEnd =\n          VmUtils.codePointOffsetToCharOffset(self, exclusiveEnd - start, charStart);\n      if (charExclusiveEnd < charStart) {\n        throw exceptionBuilder()\n            .evalError(\n                \"charIndexOutOfRange\", exclusiveEnd, start, self.codePointCount(0, self.length()))\n            .withProgramValue(\"String\", self)\n            .build();\n      }\n\n      return self.substring(charStart, charExclusiveEnd);\n    }\n  }","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-core/src/main/java/org/pkl/core/stdlib/base/StringNodes.java#L223-L259","documentation":"String.substring(start, exclusiveEnd) takes code-point offsets. This error is thrown when the `start` offset does not fall on a code-point boundary or is beyond the string's code-point count, i.e. the start index is out of the valid range 0..length. The error message includes the offending start, the valid maximum, and the string itself.","triggerScenarios":"Calling substring with start < 0, start > string length, or start landing inside a surrogate pair (slicing computed from UTF-16/byte indices instead of code points, e.g. after using .length on a UTF-16 view of the value).","commonSituations":"Slicing strings containing emoji or other astral characters using byte or UTF-16 positions; off-by-one loops computing start from endIndex of a previous substring; config-driven offsets that exceed the actual value length.","solutions":["Validate `0 <= start <= str.length` (Pkl code-point length) before calling substring.","Use take/drop, takeLast/dropLast or substringOrNull when bounds may be uncertain — they clamp or return null instead of throwing.","Compute offsets with code-point-aware APIs (e.g. indexOf results) rather than byte/UTF-16 indices.","If input is external, sanitize/trim it first so offsets derive from the final string."],"exampleFix":"// before\nname.substring(start, start + 3) // start may exceed length\n// after\nname.substringOrNull(start, start + 3) ?? \"\" // null-safe slice","handlingStrategy":"validation","validationCode":"// Pkl\nfunction validStart(s: String, start: Int): Boolean = start >= 0 && start <= s.length\n// call site\nif (validStart(str, start)) str.substringOrNull(start, end) else \"\"","typeGuard":"function inRange(i: Int, len: Int): Boolean = i >= 0 && i <= len","tryCatchPattern":"try { str.substring(start, end) } catch (e: PklError) { fallbackSlice(str, start, end) }","preventionTips":["Use substringOrNull/take/drop when bounds may be out of range","Compute offsets in code points, never bytes or UTF-16 units","Be extra careful with emoji/astral characters when slicing","Clamp external offsets to 0..str.length before slicing"],"tags":["pkl","string","index-out-of-range"],"backgroundTag":"index-out-of-range","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}