{"record":{"id":"144644dbc851b817","repo":"apple/pkl","slug":"charindexoutofrange","errorCode":"charIndexOutOfRange","errorMessage":"charIndexOutOfRange ${index} 0 ${max}","messagePattern":"charIndexOutOfRange (.+?) 0 (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkl-core/src/main/java/org/pkl/core/ast/expression/binary/SubscriptNode.java","lineNumber":43,"sourceCode":"import com.oracle.truffle.api.source.SourceSection;\nimport org.jspecify.annotations.Nullable;\nimport org.pkl.core.runtime.*;\nimport org.pkl.core.runtime.VmReference.VmReferenceAccessError;\nimport org.pkl.core.runtime.VmReference.VmReferenceAccessErrorType;\nimport org.pkl.core.util.ErrorMessages;\n\n@NodeInfo(shortName = \"[]\")\npublic abstract class SubscriptNode extends BinaryExpressionNode {\n  protected SubscriptNode(SourceSection sourceSection) {\n    super(sourceSection);\n  }\n\n  @Specialization\n  @TruffleBoundary\n  protected String eval(String receiver, long index) {\n    var charIndex = VmUtils.codePointOffsetToCharOffset(receiver, index);\n    if (charIndex == -1 || charIndex == receiver.length()) {\n      throw exceptionBuilder()\n          .evalError(\n              \"charIndexOutOfRange\", index, 0, receiver.codePointCount(0, receiver.length()) - 1)\n          .withSourceSection(getRightNode().getSourceSection())\n          .withProgramValue(\"String\", receiver)\n          .build();\n    }\n\n    if (Character.isHighSurrogate(receiver.charAt(charIndex))\n        && charIndex < receiver.length() - 1\n        && Character.isLowSurrogate(receiver.charAt(charIndex + 1))) {\n      return receiver.substring(charIndex, charIndex + 2);\n    }\n    return receiver.substring(charIndex, charIndex + 1);\n  }\n\n  @Specialization\n  protected Object eval(VmList receiver, long index) {\n    if (index < 0 || index >= receiver.getLength()) {","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-core/src/main/java/org/pkl/core/ast/expression/binary/SubscriptNode.java#L25-L61","documentation":"Indexing a String with `[i]` in Pkl indexes code points; when the index is negative, beyond the last code point, or falls inside a surrogate pair boundary, Pkl throws `charIndexOutOfRange` showing the valid range 0..max. It protects against out-of-bounds character access.","triggerScenarios":"`s[index]` where index < 0 or index >= number of code points, e.g. `\"abc\"[5]` or `\"abc\"[-1]`.","commonSituations":"Off-by-one errors using `length` instead of `length - 1`, parsing possibly-empty strings, or indexing user-supplied offsets.","solutions":["Bounds-check the index against `s.length - 1` (actually codePointCount - 1) before indexing","Handle the empty-string case separately","Use `s.substring(...)` or iterate instead of direct indexing when bounds are uncertain"],"exampleFix":"// before (Pkl)\nval c = name[name.length - 1]\n// after\nval c = name.isEmpty() ? null : name[name.length - 1]","handlingStrategy":"validation","validationCode":"function safeCharAt(s, i) {\n  if (typeof s !== 'string' || !Number.isInteger(i)) return null;\n  return (i >= 0 && i < s.length) ? s[i] : null;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Remember valid range is 0..(codePointCount - 1)","Handle empty strings before indexing","Prefer substring/iteration over positional access"],"tags":["string","index-out-of-range","pkl"],"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"}