elastic/elasticsearch · error · RuntimeException

Invalid Mapping Rule : [{rule}]. Illegal type.

Error message

Invalid Mapping Rule : [{rule}]. Illegal type.

What it means

The right-hand side of a type_table rule must be one of the recognised type identifiers (parseType returns a Byte for known names and null otherwise). When parseType returns null, parseTypes throws RuntimeException. Known names include LOWER, UPPER, ALPHA, DIGIT, SUBWORD_DELIM (and a few others) — anything else is rejected.

Source

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

    }

    // 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;
        else if (s.equals("ALPHA")) return WordDelimiterFilter.ALPHA;

View on GitHub (pinned to db6a809a66)

Solutions

  1. Use one of the recognised type names (LOWER, UPPER, ALPHA, DIGIT, SUBWORD_DELIM) on the RHS.
  2. Match the casing exactly (these are uppercase constants).
  3. Re-run the index/template create.

Example fix

// before
"type_table": ["- => PUNCT"]
// after
"type_table": ["- => SUBWORD_DELIM"]
Defensive patterns

Strategy: validation

Validate before calling

static final Set<String> VALID_TYPES = Set.of("LOWER","UPPER","ALPHA","DIGIT","SUBWORD_DELIM");
static List<String> badRhs(List<String> typeTable) {
  return typeTable.stream()
    .map(r -> r.split("=>", 2)[1].trim())
    .filter(rhs -> !VALID_TYPES.contains(rhs))
    .toList();
}

Prevention

When it happens

Trigger: A type_table entry whose RHS is not a valid type constant: "- => PUNCT", "- => lower", "- => DASH".

Common situations: Typo in the type name; using lowercase; inventing a type that does not exist in WordDelimiterFilter.

Related errors


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