{"record":{"id":"f02de1cce14a1eb6","repo":"elastic/elasticsearch","slug":"failed-to-build-synonyms-from-rules-origin","errorCode":null,"errorMessage":"failed to build synonyms from [{rules.origin}]","messagePattern":"failed to build synonyms from \\[(.+?)\\]","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/SynonymTokenFilterFactory.java","lineNumber":316,"sourceCode":"    SynonymMap buildSynonyms(Analyzer analyzer, ReaderWithOrigin rules) {\n        try {\n            SynonymMap.Builder parser;\n            if (\"wordnet\".equalsIgnoreCase(format)) {\n                parser = new ESWordnetSynonymParser(true, expand, lenient, analyzer, circuitBreaker);\n                ((ESWordnetSynonymParser) parser).parse(rules.reader);\n            } else {\n                parser = new ESSolrSynonymParser(true, expand, lenient, analyzer, circuitBreaker);\n                ((ESSolrSynonymParser) parser).parse(rules.reader);\n            }\n            return parser.build();\n        } catch (Exception e) {\n            String message = \"failed to build synonyms from [\" + rules.origin + \"]\";\n            if (lenient && e instanceof CircuitBreakingException) {\n                LOGGER.error(message + \". Using an empty synonyms map in its place because lenient=true.\", e);\n                return EMPTY_SYNONYM_MAP;\n            }\n\n            throw new IllegalArgumentException(message, e);\n        }\n    }\n\n    record ReaderWithOrigin(Reader reader, String origin, Set<String> resources) {\n        ReaderWithOrigin(Reader reader, String origin) {\n            this(reader, origin, Set.of());\n        }\n    }\n\n    private static SynonymMap buildEmptySynonymMap() {\n        try {\n            return new SynonymMap.Builder().build();\n        } catch (IOException e) {\n            throw new UncheckedIOException(e);\n        }\n    }\n}\n","sourceCodeStart":298,"sourceCodeEnd":334,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/SynonymTokenFilterFactory.java#L298-L334","documentation":"Any exception raised while parsing or building the Solr/Wordnet SynonymMap (parser.parse(reader) or parser.build()) is caught and rethrown as IllegalArgumentException with the offending origin label. The only lenient path is when lenient=true AND the cause is a CircuitBreakingException (heap pressure) — in that case an empty synonym map is substituted and an ERROR logged. All other parse/format errors propagate.","triggerScenarios":"Loading a synonyms file or inline list whose rule syntax the chosen parser rejects: malformed Solr-style mappings ('a =>' with no RHS, stray commas), unparseable Wordnet file, duplicate or contradictory rules, or heap exhaustion during SynonymMap.build() with lenient=false.","commonSituations":"Editing a synonyms file by hand and breaking the 'lhs => rhs' or 'a,b,c' syntax; switching format between Solr and Wordnet without updating content; loading a multi-GB synonym set on a memory-constrained node; encoding issues (BOM, non-UTF8) in synonyms_path files.","solutions":["Inspect the origin in the message ([file:path], [inline], or [set:name]) and open that source to find the offending rule.","Validate each line: Solr format is comma-separated equivalents OR 'lhs => rhs'; ensure no stray punctuation.","If the cause is CircuitBreakingException and you can tolerate dropping synonyms, set \"lenient\": true on the filter to fall back to an empty map.","Re-save the file as UTF-8 without BOM if encoding is suspect."],"exampleFix":"// before (malformed)\n\"synonyms\": [\"car => automobile,\", \"socks sock\"]\n// after\n\"synonyms\": [\"car,automobile\", \"socks,sock\"]","handlingStrategy":"try-catch","validationCode":"// Validate Solr-style lines before sending\nstatic List<String> badLines(List<String> lines) {\n  List<String> bad = new ArrayList<>();\n  for (String l : lines) {\n    String t = l.trim();\n    if (t.isEmpty()) continue;\n    boolean ok = t.contains(\",\") || (t.contains(\"=>\") && t.split(\"=>\",2)[1].trim().length() > 0);\n    if (!ok) bad.add(l);\n  }\n  return bad;\n}","typeGuard":null,"tryCatchPattern":"// Wrap index creation that includes a synonym filter\ntry {\n  client.indices().create(c);\n} catch (ResponseException e) {\n  if (e.getResponse().getStatusLine().getStatusCode() == 400\n      && e.getMessage().contains(\"failed to build synonyms\")) {\n    // surface the offending origin to the operator; do not retry unchanged\n    reportSynonymParseFailure(e);\n  } else throw e;\n}","preventionTips":["Validate synonym file syntax in CI before deploying to nodes' config directories.","Prefer synonyms_set (managed via PUT _synonyms) so rule edits are validated at write time.","Consider lenient=true only when you accept silent empty-map fallback under memory pressure."],"tags":["analysis","synonyms","parsing","circuit-breaker"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}