apple/pkl · error

errorInRegexReplacement

errorInRegexReplacement

Error message

errorInRegexReplacement

What it means

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.

Source

Thrown at pkl-core/src/main/java/org/pkl/core/stdlib/base/StringNodes.java:538

      var idx = findOffset(self, function);
      return substringUntil(self, idx);
    }
  }

  public abstract static class replaceAll extends ExternalMethod2Node {
    @TruffleBoundary
    @Specialization
    protected String eval(String self, String pattern, String replacement) {
      return self.replace(pattern, replacement);
    }

    @TruffleBoundary
    @Specialization
    protected String eval(String self, VmRegex regex, String replacement) {
      try {
        return regex.matcher(self).replaceAll(replacement);
      } catch (IndexOutOfBoundsException | IllegalArgumentException e) {
        throw exceptionBuilder()
            .evalError(
                "errorInRegexReplacement",
                regex.getPattern().toString(),
                replacement,
                e.getMessage())
            .build();
      }
    }
  }

  public abstract static class replaceFirst extends ExternalMethod2Node {
    @TruffleBoundary
    @Specialization
    protected String eval(String self, String pattern, String replacement) {
      int idx = self.indexOf(pattern);
      if (idx == -1) return self;
      return self.substring(0, idx) + replacement + self.substring(idx + pattern.length());
    }

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Escape `$` as `$$` and `\` as `\\` in the replacement string before calling replaceAll.
  2. When the replacement is plain text, use replaceAll(literalPattern, replacement) with a String pattern instead of a Regex, which does literal replacement.
  3. Sanitize dynamic replacements, e.g. replacement.replaceAll("\\$", "$$").replaceAll("\\\\", "\\\\\\\\").
  4. If you need capture groups, verify the regex actually defines the group referenced by $n (n <= group count).

Example fix

// before
s.replaceAll(Regex("price"), "cost: $5") // $5 = invalid group ref
// after
s.replaceAll(Regex("price"), "cost: $$5") // or use a literal String pattern
Defensive patterns

Strategy: validation

Validate before calling

// Pkl: escape replacement specials before use
function escapeReplacement(r: String): String =
  r.replaceAll("\\\\", "\\\\\\\\").replaceAll("\\$", "$$")
// call site
s.replaceAll(Regex("price"), escapeReplacement(userText))

Type guard

function isSafeReplacement(r: String): Boolean = !r.contains("$") && !r.endsWith("\\")

Try / catch

try { s.replaceAll(regex, repl) } catch (e: PklError) { log(e.message); s }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08). Data as JSON: /api/errors/3bc68e913c3d83b7. Report an issue: GitHub.