elastic/elasticsearch · error · RuntimeException

Invalid escaped char in [{s}]

Error message

Invalid escaped char in [{s}]

What it means

parseString processes backslash escapes inside type_table LHS strings. When a backslash is encountered at the final position (readPos >= len after consuming the '\'), there is no character left to escape, so it throws RuntimeException. The next char must be one of \\, n, t, r, b, f, u; a dangling backslash has no successor at all.

Source

Thrown at modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/WordDelimiterTokenFilterFactory.java:155

    private static Byte parseType(String s) {
        if (s.equals("LOWER")) return WordDelimiterFilter.LOWER;
        else if (s.equals("UPPER")) return WordDelimiterFilter.UPPER;
        else if (s.equals("ALPHA")) return WordDelimiterFilter.ALPHA;
        else if (s.equals("DIGIT")) return WordDelimiterFilter.DIGIT;
        else if (s.equals("ALPHANUM")) return WordDelimiterFilter.ALPHANUM;
        else if (s.equals("SUBWORD_DELIM")) return WordDelimiterFilter.SUBWORD_DELIM;
        else return null;
    }

    private static String parseString(String s) {
        char[] out = new char[256];
        int readPos = 0;
        int len = s.length();
        int writePos = 0;
        while (readPos < len) {
            char c = s.charAt(readPos++);
            if (c == '\\') {
                if (readPos >= len) throw new RuntimeException("Invalid escaped char in [" + s + "]");
                c = s.charAt(readPos++);
                switch (c) {
                    case '\\' -> c = '\\';
                    case 'n' -> c = '\n';
                    case 't' -> c = '\t';
                    case 'r' -> c = '\r';
                    case 'b' -> c = '\b';
                    case 'f' -> c = '\f';
                    case 'u' -> {
                        if (readPos + 3 >= len) throw new RuntimeException("Invalid escaped char in [" + s + "]");
                        c = (char) Integer.parseInt(s.substring(readPos, readPos + 4), 16);
                        readPos += 4;
                    }
                }
            }
            out[writePos++] = c;
        }
        return new String(out, 0, writePos);

View on GitHub (pinned to db6a809a66)

Solutions

  1. Double every literal backslash: write '\\\\' in JSON for a single '\\' character.
  2. Use the appropriate escape sequence (\\n, \\t, \\uXXXX) where you intended a control character.
  3. Validate the JSON-parsed value of each type_table LHS before sending.

Example fix

// before (intended: map backslash to ALPHA; under-escaped)
"type_table": ["\ => ALPHA"]
// after
"type_table": ["\\ => ALPHA"]
Defensive patterns

Strategy: validation

Validate before calling

// A lone trailing backslash (after JSON parsing) will trip the parser
static boolean hasDanglingBackslash(String lhs) {
  for (int i = 0; i < lhs.length(); i++) {
    char c = lhs.charAt(i);
    if (c == '\\') {
      if (i + 1 >= lhs.length()) return true;
      char n = lhs.charAt(i + 1);
      if ("\\ntrbfu".indexOf(n) < 0) return true; // unknown escape also unsafe
      i++; // consume the escaped char
    }
  }
  return false;
}

Prevention

When it happens

Trigger: A type_table entry whose LHS ends with a lone backslash: "\\ => ALPHA" intended as a single backslash but written as a single '\'; or any LHS containing '\' not followed by an escape character.

Common situations: JSON-level under-escaping: a single backslash in the JSON string becomes a lone backslash in the parsed value; copy-pasting Windows-style paths or regex-style escapes without doubling.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/c3d90ee38418ba94. Report an issue: GitHub.