{"record":{"id":"3bc68e913c3d83b7","repo":"apple/pkl","slug":"errorinregexreplacement","errorCode":"errorInRegexReplacement","errorMessage":"errorInRegexReplacement","messagePattern":"errorInRegexReplacement","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkl-core/src/main/java/org/pkl/core/stdlib/base/StringNodes.java","lineNumber":538,"sourceCode":"      var idx = findOffset(self, function);\n      return substringUntil(self, idx);\n    }\n  }\n\n  public abstract static class replaceAll extends ExternalMethod2Node {\n    @TruffleBoundary\n    @Specialization\n    protected String eval(String self, String pattern, String replacement) {\n      return self.replace(pattern, replacement);\n    }\n\n    @TruffleBoundary\n    @Specialization\n    protected String eval(String self, VmRegex regex, String replacement) {\n      try {\n        return regex.matcher(self).replaceAll(replacement);\n      } catch (IndexOutOfBoundsException | IllegalArgumentException e) {\n        throw exceptionBuilder()\n            .evalError(\n                \"errorInRegexReplacement\",\n                regex.getPattern().toString(),\n                replacement,\n                e.getMessage())\n            .build();\n      }\n    }\n  }\n\n  public abstract static class replaceFirst extends ExternalMethod2Node {\n    @TruffleBoundary\n    @Specialization\n    protected String eval(String self, String pattern, String replacement) {\n      int idx = self.indexOf(pattern);\n      if (idx == -1) return self;\n      return self.substring(0, idx) + replacement + self.substring(idx + pattern.length());\n    }","sourceCodeStart":520,"sourceCodeEnd":556,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-core/src/main/java/org/pkl/core/stdlib/base/StringNodes.java#L520-L556","documentation":"String.replaceAll(regex, replacement) wraps Java's Matcher.replaceAll; if the replacement string is itself invalid as a Java/Pkl regex replacement template, the underlying IndexOutOfBoundsException or IllegalArgumentException is rethrown as errorInRegexReplacement with the pattern, the offending replacement, and the underlying message. The pattern itself compiles fine — the failure is in the replacement template.","triggerScenarios":"Replacement strings containing a bare `$` (dangling group reference like \"$\" or \"$1\" with no group 1), a `$` followed by an invalid character, or a trailing lone `\\` (invalid escape sequence), e.g. \"prices from $5\" or Windows-style paths used as replacement text.","commonSituations":"Substituting user-provided text (paths, currency amounts, shell snippets) into regex replacements; templating config values that literally contain $ or \\; generating code/config where `$` is currency, not a capture group.","solutions":["Escape `$` as `$$` and `\\` as `\\\\` in the replacement string before calling replaceAll.","When the replacement is plain text, use replaceAll(literalPattern, replacement) with a String pattern instead of a Regex, which does literal replacement.","Sanitize dynamic replacements, e.g. replacement.replaceAll(\"\\\\$\", \"$$\").replaceAll(\"\\\\\\\\\", \"\\\\\\\\\\\\\\\\\").","If you need capture groups, verify the regex actually defines the group referenced by $n (n <= group count)."],"exampleFix":"// before\ns.replaceAll(Regex(\"price\"), \"cost: $5\") // $5 = invalid group ref\n// after\ns.replaceAll(Regex(\"price\"), \"cost: $$5\") // or use a literal String pattern","handlingStrategy":"validation","validationCode":"// Pkl: escape replacement specials before use\nfunction escapeReplacement(r: String): String =\n  r.replaceAll(\"\\\\\\\\\", \"\\\\\\\\\\\\\\\\\").replaceAll(\"\\\\$\", \"$$\")\n// call site\ns.replaceAll(Regex(\"price\"), escapeReplacement(userText))","typeGuard":"function isSafeReplacement(r: String): Boolean = !r.contains(\"$\") && !r.endsWith(\"\\\\\")","tryCatchPattern":"try { s.replaceAll(regex, repl) } catch (e: PklError) { log(e.message); s }","preventionTips":["Escape $ as $$ and backslash as \\\\ in dynamic replacements","Use the String (literal) overload of replaceAll for plain-text replacements","Verify $n references match existing regex groups","Never substitute raw user/path input into regex replacement templates unescaped"],"tags":["pkl","string","regex","replacement-template"],"backgroundTag":"invalid-regex-pattern","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}