{"record":{"id":"07d6bf9d90da19cb","repo":"languagetool-org/languagetool","slug":"contradicting-regex-contains-both-i-case-insen","errorCode":null,"errorMessage":"Contradicting regex contains both '?i' (case-insensitive) and \\p{Lu}/\\p{Ll} (case-sensitive): + s + in rule + id","messagePattern":"Contradicting regex contains both '\\?i' \\(case-insensitive\\) and \\\\p(.+?)/\\\\p(.+?) \\(case-sensitive\\): \\+ s \\+ in rule \\+ id","errorType":"validation","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"languagetool-core/src/main/java/org/languagetool/rules/patterns/XMLRuleHandler.java","lineNumber":502,"sourceCode":"      PatternToken exception = new PatternToken(exceptionStringInflected, internMatcher(exceptions.toString().trim(), exceptionStringRegExp, caseSensitive));\n      exception.setNegation(exceptionStringNegation);\n      exception.setPosToken(obtainPosToken(exceptionPosToken, exceptionPosRegExp, exceptionPosNegation));\n      patternToken.addException(exceptionValidNext, exceptionValidPrev, exception);\n      exceptionPosToken = null;\n      exceptionLevelCaseSensitive = null;\n    }\n    if (exceptionSpaceBeforeSet) {\n      patternToken.setExceptionSpaceBefore(exceptionSpaceBefore);\n    }\n    resetException();\n  }\n\n  // To be compatible with Java 15, we make p{Lu} and p{Ll} imply case-sensitivity,\n  // see https://github.com/languagetool-org/languagetool/issues/4061\n  private boolean hasPosixCharacterClass(String s) {\n    boolean res = s.contains(\"\\\\p{Lu}\") || s.contains(\"\\\\p{Ll}\");\n    if (res && s.contains(\"(?i\")) {\n      throw new RuntimeException(\"Contradicting regex contains both '?i' (case-insensitive) and \\\\p{Lu}/\\\\p{Ll} (case-sensitive): \"\n              + s + \" in rule \" + id);\n    }\n    return res;\n  }\n\n  protected void setToken(Attributes attrs) throws SAXException {\n    inToken = true;\n    if (lastPhrase) {\n      patternTokens.clear();\n    }\n    lastPhrase = false;\n    tokenNegated = YES.equals(attrs.getValue(NEGATE));\n    tokenInflected = YES.equals(attrs.getValue(INFLECTED));\n    if (attrs.getValue(SKIP) != null) {\n      skipPos = Integer.parseInt(attrs.getValue(SKIP));\n    }\n    if (attrs.getValue(MIN) != null) {\n      minOccurrence = Integer.parseInt(attrs.getValue(MIN));","sourceCodeStart":484,"sourceCodeEnd":520,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-core/src/main/java/org/languagetool/rules/patterns/XMLRuleHandler.java#L484-L520","documentation":"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.","triggerScenarios":"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.","commonSituations":"Upgrading LanguageTool/Java where regex semantics changed (issue #4061); combining previously working (?i) regexes with case-class tokens; reusing regexes from other tools.","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"],"exampleFix":"// before\n<token regex=\"(?i)\\p{Lu}\\p{Lu}+\"/>\n// after\n<token regex=\"\\p{Lu}\\p{Lu}+\"/>","handlingStrategy":"validation","validationCode":"if (regex.contains(\"(?i\") && (regex.contains(\"\\\\p{Lu}\") || regex.contains(\"\\\\p{Ll}\"))) {\n  throw new IllegalArgumentException(\"Case-insensitive flag combined with \\\\p{Lu}/\\\\p{Ll}: \" + regex);\n}","typeGuard":null,"tryCatchPattern":"try {\n  loader.getRules(is, filename);\n} catch (RuntimeException e) {\n  if (e.getMessage().contains(\"Contradicting regex\")) {\n    log.error(\"Remove (?i) or the POSIX case classes in the regex\", e);\n  } else throw e;\n}","preventionTips":["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)"],"tags":["languagetool","regex","case-sensitivity","contradiction"],"backgroundTag":"invalid-regex-pattern","analyzedSha":"2e990059ce67d5e2a0f7f7ca5d31160c6709df4b","analyzedAt":"2026-09-06T09:20:17.015Z","contentChangedAt":"2026-09-06T09:20:17.015Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}