languagetool-org/languagetool · error · RuntimeException
Contradicting regex contains both '?i' (case-insensitive) an
Error message
Contradicting regex contains both '?i' (case-insensitive) and \p{Lu}/\p{Ll} (case-sensitive): + s + in rule + id What it means
XMLRuleHandler.hasPosixCharacterClass throws this RuntimeException when a regex in a rule combines the case-insensitive flag '(?i)' with POSIX-style case-sensitive character classes \p{Lu} or \p{Ll}. To be compatible with Java 15+, LanguageTool treats \p{Lu}/\p{Ll} as implying case sensitivity, so mixing them with (?i) is contradictory and rejected at rule load time.
Source
Thrown at languagetool-core/src/main/java/org/languagetool/rules/patterns/XMLRuleHandler.java:502
PatternToken exception = new PatternToken(exceptionStringInflected, internMatcher(exceptions.toString().trim(), exceptionStringRegExp, caseSensitive));
exception.setNegation(exceptionStringNegation);
exception.setPosToken(obtainPosToken(exceptionPosToken, exceptionPosRegExp, exceptionPosNegation));
patternToken.addException(exceptionValidNext, exceptionValidPrev, exception);
exceptionPosToken = null;
exceptionLevelCaseSensitive = null;
}
if (exceptionSpaceBeforeSet) {
patternToken.setExceptionSpaceBefore(exceptionSpaceBefore);
}
resetException();
}
// To be compatible with Java 15, we make p{Lu} and p{Ll} imply case-sensitivity,
// see https://github.com/languagetool-org/languagetool/issues/4061
private boolean hasPosixCharacterClass(String s) {
boolean res = s.contains("\\p{Lu}") || s.contains("\\p{Ll}");
if (res && s.contains("(?i")) {
throw new RuntimeException("Contradicting regex contains both '?i' (case-insensitive) and \\p{Lu}/\\p{Ll} (case-sensitive): "
+ s + " in rule " + id);
}
return res;
}
protected void setToken(Attributes attrs) throws SAXException {
inToken = true;
if (lastPhrase) {
patternTokens.clear();
}
lastPhrase = false;
tokenNegated = YES.equals(attrs.getValue(NEGATE));
tokenInflected = YES.equals(attrs.getValue(INFLECTED));
if (attrs.getValue(SKIP) != null) {
skipPos = Integer.parseInt(attrs.getValue(SKIP));
}
if (attrs.getValue(MIN) != null) {
minOccurrence = Integer.parseInt(attrs.getValue(MIN));View on GitHub (pinned to 2e990059ce)
Solutions
- Remove the '(?i)' flag and spell out both cases explicitly (e.g. [\p{Lu}\p{Ll}]) or keep \p{Lu}/\p{Ll} only
- Alternatively drop \p{Lu}/\p{Ll} and rely on (?i) if case does not matter
- Test the regex in isolation with a small Java snippet or regex101 to confirm intended case behavior
Example fix
// before
<token regex="(?i)\p{Lu}\p{Lu}+"/>
// after
<token regex="\p{Lu}\p{Lu}+"/> Defensive patterns
Strategy: validation
Validate before calling
if (regex.contains("(?i") && (regex.contains("\\p{Lu}") || regex.contains("\\p{Ll}"))) {
throw new IllegalArgumentException("Case-insensitive flag combined with \\p{Lu}/\\p{Ll}: " + regex);
} Try / catch
try {
loader.getRules(is, filename);
} catch (RuntimeException e) {
if (e.getMessage().contains("Contradicting regex")) {
log.error("Remove (?i) or the POSIX case classes in the regex", e);
} else throw e;
} Prevention
- Never combine (?i) with \p{Lu}/\p{Ll} in rule regexes
- Prefer explicit case classes without (?i) when case matters
- Test regexes against your target Java version (Java 15+ semantics)
When it happens
Trigger: A token with regex="...(?i)...\p{Lu}..." (or postag/exception regex containing both) is parsed in setToken/hasPosixCharacterClass for a rule with the given id.
Common situations: Upgrading LanguageTool/Java where regex semantics changed (issue #4061); combining previously working (?i) regexes with case-class tokens; reusing regexes from other tools.
Related errors
- Got " + matcher.groupCount() + " groups for regex '" + patte
- Got " + matcher.groupCount() + " groups for regex '" + patte
- suppressMisspelledMatch must be a valid regex
- suppressMisspelledSuggestions must be a valid regex
- Format error in file ${path}, line: ${line}
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/07d6bf9d90da19cb.
Report an issue: GitHub.