elastic/elasticsearch · error · RuntimeException
Invalid Mapping Rule : [{rule}]. Only a single character is
Error message
Invalid Mapping Rule : [{rule}]. Only a single character is allowed. What it means
After a type_table rule matches the regex, the left-hand side is run through parseString (handling escapes) and must yield exactly one character. If lhs.length() != 1 parseTypes throws RuntimeException. Multi-character LHS is structurally invalid because each rule maps a single char to a single byte type.
Source
Thrown at modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/WordDelimiterTokenFilterFactory.java:121
return 0;
}
// source => type
private static final Pattern typePattern = Pattern.compile("(.*)\\s*=>\\s*(.*)\\s*$");
/**
* parses a list of MappingCharFilter style rules into a custom byte[] type table
*/
static byte[] parseTypes(Collection<String> rules) {
SortedMap<Character, Byte> typeMap = new TreeMap<>();
for (String rule : rules) {
Matcher m = typePattern.matcher(rule);
if (m.find() == false) {
throw new RuntimeException("Invalid Mapping Rule : [" + rule + "]");
}
String lhs = parseString(m.group(1).trim());
Byte rhs = parseType(m.group(2).trim());
if (lhs.length() != 1) throw new RuntimeException("Invalid Mapping Rule : [" + rule + "]. Only a single character is allowed.");
if (rhs == null) throw new RuntimeException("Invalid Mapping Rule : [" + rule + "]. Illegal type.");
typeMap.put(lhs.charAt(0), rhs);
}
// ensure the table is always at least as big as DEFAULT_WORD_DELIM_TABLE for performance
byte types[] = new byte[Math.max(typeMap.lastKey() + 1, WordDelimiterIterator.DEFAULT_WORD_DELIM_TABLE.length)];
for (int i = 0; i < types.length; i++) {
types[i] = WordDelimiterIterator.getType(i);
}
for (Map.Entry<Character, Byte> mapping : typeMap.entrySet()) {
types[mapping.getKey()] = mapping.getValue();
}
return types;
}
private static Byte parseType(String s) {
if (s.equals("LOWER")) return WordDelimiterFilter.LOWER;
else if (s.equals("UPPER")) return WordDelimiterFilter.UPPER;View on GitHub (pinned to db6a809a66)
Solutions
- Provide exactly one character (or one escape sequence) on the LHS of each rule.
- For multi-character patterns use a different mechanism (protected words, custom analyzer), not type_table.
- Re-validate each entry's LHS length.
Example fix
// before "type_table": ["abc => ALPHA"] // after "type_table": ["a => ALPHA", "b => ALPHA", "c => ALPHA"]
Defensive patterns
Strategy: validation
Validate before calling
// After resolving escapes, each LHS must be exactly one char
static List<String> badLhs(List<String> typeTable) {
return typeTable.stream()
.map(r -> r.split("=>", 2)[0].trim())
.filter(lhs -> resolveEscapes(lhs).length() != 1)
.toList();
} Prevention
- Remember type_table maps one character to one type — not sequences.
- Use protected_words or a different filter for multi-char patterns.
When it happens
Trigger: A type_table entry whose LHS decodes to two or more characters: "abc => LOWER", ".. => DIGIT", or an escape that resolves to a surrogate pair.
Common situations: Trying to map a whole word or punctuation sequence to a type; misunderstanding that type_table is per-character.
Related errors
- Invalid Mapping Rule : [{rule}]. Illegal type.
- Invalid Mapping Rule : [{rule}]
- Invalid escaped char in [{s}]
- length parameter must be provided
- Token filter [{name()}] cannot be used to parse synonyms
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/1dd7a14fd6ac2cff.
Report an issue: GitHub.