{"record":{"id":"4d0b547fe8336921","repo":"languagetool-org/languagetool","slug":"confusioncheckfilter-index-out-of-bounds-in-m","errorCode":null,"errorMessage":"ConfusionCheckFilter: Index out of bounds in \" + match.getRule().getFullId() + \", value: \" + i","messagePattern":"ConfusionCheckFilter: Index out of bounds in \" \\+ match\\.getRule\\(\\)\\.getFullId\\(\\) \\+ \", value: \" \\+ i","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"languagetool-language-modules/es/src/main/java/org/languagetool/rules/es/ConfusionCheckFilter.java","lineNumber":58,"sourceCode":"  private static final Pattern CP = Pattern.compile(\"NC[MFC][PN]000|A..[MFC][PN].|V.P..P.\");\n  private static final Pattern CS = Pattern.compile(\"NC[MFC][SN]000|A..[MFC][SN].|V.P..S.\");\n\n  @Override\n  public RuleMatch acceptRuleMatch(RuleMatch match, Map<String, String> arguments, int patternTokenPos,\n                                   AnalyzedTokenReadings[] patternTokens, List<Integer> tokenPositions) {\n\n    Pattern desiredGenderNumberPattern = null;\n    String replacement = null;\n    String postag = getRequired(\"postag\", arguments);\n    String originalForm = getRequired(\"form\", arguments);\n    boolean isAllUppercase = StringTools.isAllUppercase(originalForm);\n    boolean isCapitalized = StringTools.isCapitalizedWord(originalForm);\n    String form = originalForm.toLowerCase();\n    String gendernumberFrom = getOptional(\"gendernumberFrom\", arguments);\n    if (gendernumberFrom != null) {\n      int i = Integer.parseInt(gendernumberFrom);\n      if (i < 1 || i > patternTokens.length) {\n        throw new IllegalArgumentException(\n            \"ConfusionCheckFilter: Index out of bounds in \" + match.getRule().getFullId() + \", value: \" + i);\n      }\n      AnalyzedTokenReadings atr = patternTokens[i - 1];\n      if (atr.matchesPosTagRegex(\"[NAPD].+MS.*|V.P..SM\")) { desiredGenderNumberPattern = MS;}\n      else if (atr.matchesPosTagRegex(\"[NAPD].+MP.*|V.P..PM\")) { desiredGenderNumberPattern = MP;}\n      else if (atr.matchesPosTagRegex(\"[NAPD].+FS.*|V.P..SF\")) { desiredGenderNumberPattern = FS;}\n      else if (atr.matchesPosTagRegex(\"[NAPD].+FP.*|V.P..PF\")) { desiredGenderNumberPattern = FP;}\n      else if (atr.matchesPosTagRegex(\"[NAPD].+CP.*|V.P..P.\")) { desiredGenderNumberPattern = CP;}\n      else if (atr.matchesPosTagRegex(\"[NAPD].+CS.*|V.P..S.\")) { desiredGenderNumberPattern = CS;}\n    }\n    \n    if (relevantWords.containsKey(form)) {\n      if (relevantWords.get(form).matchesPosTagRegex(postag)) {\n        if (desiredGenderNumberPattern != null) {\n          Matcher m = desiredGenderNumberPattern.matcher(relevantWords.get(form).getReadings().get(0).getPOSTag());\n          if (!m.matches()) {\n            return null;\n          }","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-language-modules/es/src/main/java/org/languagetool/rules/es/ConfusionCheckFilter.java#L40-L76","documentation":"ConfusionCheckFilter.acceptRuleMatch validates the gendernumberFrom argument supplied by a Spanish confusion-pair rule before indexing patternTokens. If the parsed value is less than 1 or greater than the number of pattern tokens, it throws IllegalArgumentException. This is an authoring/data error in the rule XML, not a runtime condition users can fix.","triggerScenarios":"A Spanish confusion rule XML declares gendernumberFrom=\"N\" where N < 1 or N exceeds the count of tokens in the rule's pattern; acceptRuleMatch parses it with Integer.parseInt and the bounds check fails.","commonSituations":"Editing or adding Spanish confusion pairs data and mis-numbering the gendernumberFrom token index (1-based); a rule pattern that was shortened after the attribute was written; porting a rule between rules with different token counts.","solutions":["Fix the gendernumberFrom attribute in the offending rule XML so it points at an existing pattern token (1-based, <= patternTokens.length).","Recount the tokens in the rule's <pattern> (including any skipped tokens per the filter's counting convention) and renumber.","Check the rule id in the message against the Spanish confusion rules and run the rule tests to verify the fix."],"exampleFix":"// before (rule XML)\n<filter class=\"org.languagetool.rules.es.ConfusionCheckFilter\" args=\"... gendernumberFrom:5 ...\"/>\n// after (pattern only has 4 tokens)\n<filter class=\"org.languagetool.rules.es.ConfusionCheckFilter\" args=\"... gendernumberFrom:2 ...\"/>","handlingStrategy":"validation","validationCode":"int i = Integer.parseInt(arguments.get(\"gendernumberFrom\"));\nif (i < 1 || i > patternTokens.length) {\n  throw new IllegalArgumentException(\"gendernumberFrom=\" + i + \" out of range 1..\" + patternTokens.length + \" in rule \" + ruleFullId);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate gendernumberFrom against the pattern token count when authoring rule XML","Run the rule's unit tests after every pattern edit","Recount token indexes after changing a rule's pattern"],"tags":["java","languagetool","rule-authoring","index-out-of-bounds"],"backgroundTag":"index-out-of-bounds","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"}