apple/pkl · error
charIndexOutOfRange
charIndexOutOfRange
Error message
charIndexOutOfRange
What it means
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.
Source
Thrown at pkl-core/src/main/java/org/pkl/core/stdlib/base/StringNodes.java:241
var charIndex = VmUtils.codePointOffsetToCharOffset(self, index);
if (charIndex == -1 || charIndex == self.length()) return VmNull.withoutDefault();
if (Character.isHighSurrogate(self.charAt(charIndex))
&& charIndex < self.length() - 1
&& Character.isLowSurrogate(self.charAt(charIndex + 1))) {
return self.substring(charIndex, charIndex + 2);
}
return self.substring(charIndex, charIndex + 1);
}
}
public abstract static class substring extends ExternalMethod2Node {
@TruffleBoundary
@Specialization
protected String eval(String self, long start, long exclusiveEnd) {
var charStart = VmUtils.codePointOffsetToCharOffset(self, start);
if (charStart == -1) {
throw exceptionBuilder()
.evalError("charIndexOutOfRange", start, 0, self.codePointCount(0, self.length()))
.withProgramValue("String", self)
.build();
}
var charExclusiveEnd =
VmUtils.codePointOffsetToCharOffset(self, exclusiveEnd - start, charStart);
if (charExclusiveEnd < charStart) {
throw exceptionBuilder()
.evalError(
"charIndexOutOfRange", exclusiveEnd, start, self.codePointCount(0, self.length()))
.withProgramValue("String", self)
.build();
}
return self.substring(charStart, charExclusiveEnd);
}
}View on GitHub (pinned to f3efcbfc9b)
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.
Example fix
// before name.substring(start, start + 3) // start may exceed length // after name.substringOrNull(start, start + 3) ?? "" // null-safe slice
Defensive patterns
Strategy: validation
Validate before calling
// Pkl function validStart(s: String, start: Int): Boolean = start >= 0 && start <= s.length // call site if (validStart(str, start)) str.substringOrNull(start, end) else ""
Type guard
function inRange(i: Int, len: Int): Boolean = i >= 0 && i <= len
Try / catch
try { str.substring(start, end) } catch (e: PklError) { fallbackSlice(str, start, end) } Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- charIndexOutOfRange
- Cannot convert pkl.base#String `%s` to java.lang.Character b
- elementIndexOutOfRange
- elementIndexOutOfRange
- elementIndexOutOfRange
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/efb19c621b306751.
Report an issue: GitHub.