{"record":{"id":"64b3f3f644c1ec38","repo":"languagetool-org/languagetool","slug":"wordwithdeterminerfilter-index-out-of-bounds-in-64b3f3","errorCode":null,"errorMessage":"WordWithDeterminerFilter: Index out of bounds in \" + match.getRule().getFullId() + \", posDeterminer: \" + posWord","messagePattern":"WordWithDeterminerFilter: Index out of bounds in \" \\+ match\\.getRule\\(\\)\\.getFullId\\(\\) \\+ \", posDeterminer: \" \\+ posWord","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"languagetool-language-modules/fr/src/main/java/org/languagetool/rules/fr/WordWithDeterminerFilter.java","lineNumber":69,"sourceCode":"  private static final List<String> rulesToCheck = Arrays.asList(\"CET_CE\", \"CE_CET\", \"MA_VOYELLE\", \"MON_NFS\", \"VIEUX\");\n\n  @Override\n  public RuleMatch acceptRuleMatch(RuleMatch match, Map<String, String> arguments, int patternTokenPos,\n                                   AnalyzedTokenReadings[] patternTokens, List<Integer> tokenPositions) throws IOException {\n    JLanguageTool lt = Languages.getLanguageForShortCode(\"fr\").createDefaultJLanguageTool();\n    String wordFrom = getRequired(\"wordFrom\", arguments);\n    String determinerFrom = getRequired(\"determinerFrom\", arguments);\n    int posWord = 0;\n    int posDeterminer = 0;\n    if (wordFrom != null && determinerFrom != null) {\n      posWord = Integer.parseInt(wordFrom);\n      if (posWord < 1 || posWord > patternTokens.length) {\n        throw new IllegalArgumentException(\"WordWithDeterminerFilter: Index out of bounds in \"\n            + match.getRule().getFullId() + \", wordFrom: \" + posWord);\n      }\n      posDeterminer = Integer.parseInt(determinerFrom);\n      if (posDeterminer < 1 || posDeterminer > patternTokens.length) {\n        throw new IllegalArgumentException(\"WordWithDeterminerFilter: Index out of bounds in \"\n            + match.getRule().getFullId() + \", posDeterminer: \" + posWord);\n      }\n    } else {\n      throw new IllegalArgumentException(\"WordWithDeterminerFilter: undefined parameters wordFrom or determinerFrom in \"\n          + match.getRule().getFullId());\n    }\n    AnalyzedTokenReadings atrDeterminer = patternTokens[posDeterminer - 1];\n    AnalyzedTokenReadings atrWord = patternTokens[posWord - 1];\n    boolean isDeterminerCapitalized = StringTools.isCapitalizedWord(atrDeterminer.getToken());\n    boolean isWordCapitalized = StringTools.isCapitalizedWord(atrWord.getToken());\n    boolean isDeterminerAllupper = StringTools.isAllUppercase(atrDeterminer.getToken())\n        && !atrDeterminer.getToken().equalsIgnoreCase(\"L'\");\n    boolean isWordAllupper = StringTools.isAllUppercase(atrWord.getToken());\n    AnalyzedToken atDeterminer = getAnalyzedToken(atrDeterminer, detPattern);\n    AnalyzedToken atWord = getAnalyzedToken(atrWord, wordPattern);\n    if (atWord == null || atDeterminer == null) {\n      throw new RuntimeException(\n          \"Error analyzing sentence: '\" + match.getSentence().getText() + \"' with rule \" + match.getRule().getFullId());","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-language-modules/fr/src/main/java/org/languagetool/rules/fr/WordWithDeterminerFilter.java#L51-L87","documentation":"The French WordWithDeterminerFilter throws this IllegalArgumentException when the rule XML's 'determinerFrom' position parsed into posDeterminer is outside 1..patternTokens.length. Note the message has a copy-paste bug: it prints the posWord variable instead of posDeterminer. It signals a malformed filter configuration in a disambiguation/rule XML file, not a runtime data problem.","triggerScenarios":"A French XML rule using the WordWithDeterminerFilter supplies a determinerFrom attribute whose value is < 1 or greater than the number of tokens matched by the pattern; acceptRuleMatch then parses and range-checks it and throws.","commonSituations":"Hand-editing custom French rule XML files; off-by-one in token positions when adding/removing a token from the pattern; copying a rule and forgetting to adjust determinerFrom; porting rules between pattern definitions.","solutions":["Open the rule XML with rule id = the id printed in the message and fix the determinerFrom value so it is between 1 and the number of pattern tokens.","Count the tokens in the rule's <pattern> and renumber wordFrom/determinerFrom after any token additions or removals.","If determinerFrom is correct but the pattern shrank, extend the pattern rather than the filter.","Test the rule with LanguageTool's rule validation tests so out-of-range positions fail at build time."],"exampleFix":"// before (rule XML)\n<filter class=\"org.languagetool.rules.fr.WordWithDeterminerFilter\" args=\"wordFrom:1 determinerFrom:7\"/>\n// after (pattern has 4 tokens)\n<filter class=\"org.languagetool.rules.fr.WordWithDeterminerFilter\" args=\"wordFrom:1 determinerFrom:3\"/>","handlingStrategy":"validation","validationCode":"int detFrom = Integer.parseInt(attrs.get(\"determinerFrom\"));\nif (detFrom < 1 || detFrom > patternTokens.length)\n  throw new IllegalArgumentException(\"determinerFrom out of range: \" + detFrom);","typeGuard":null,"tryCatchPattern":"try { filter.acceptRuleMatch(match); } catch (IllegalArgumentException e) { log.error(\"Bad filter config: \" + e.getMessage()); }","preventionTips":["Renumber filter positions whenever you edit a rule's token pattern","Validate rule XML with LanguageTool's rule tests in CI","Remember positions are 1-based"],"tags":["java","languagetool","rule-configuration","french"],"backgroundTag":"invalid-argument-value","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"}