{"record":{"id":"b55176cfe3ba8d8a","repo":"elastic/elasticsearch","slug":"pattern-has-an-invalid-syntax","errorCode":null,"errorMessage":"pattern [{}] has an invalid syntax","messagePattern":"pattern \\[(.+?)\\] has an invalid syntax","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"libs/grok/src/main/java/org/elasticsearch/grok/PatternBank.java","lineNumber":174,"sourceCode":"     * are found, an empty array is returned. If any of the list of pattern names to be returned does not exist in the bank, an exception\n     * is thrown.\n     */\n    private static String[] getPatternNamesForPattern(Map<String, String> bank, String patternName) {\n        String pattern = bank.get(patternName);\n        List<String> patternReferences = new ArrayList<>();\n        for (int i = pattern.indexOf(\"%{\"); i != -1; i = pattern.indexOf(\"%{\", i + 1)) {\n            int begin = i + 2;\n            int bracketIndex = pattern.indexOf('}', begin);\n            int columnIndex = pattern.indexOf(':', begin);\n            int end;\n            if (bracketIndex != -1 && columnIndex == -1) {\n                end = bracketIndex;\n            } else if (columnIndex != -1 && bracketIndex == -1) {\n                end = columnIndex;\n            } else if (bracketIndex != -1) {\n                end = Math.min(bracketIndex, columnIndex);\n            } else {\n                throw new IllegalArgumentException(\"pattern [\" + pattern + \"] has an invalid syntax\");\n            }\n            String otherPatternName = pattern.substring(begin, end);\n            if (patternReferences.contains(otherPatternName) == false) {\n                patternReferences.add(otherPatternName);\n                String otherPattern = bank.get(otherPatternName);\n                if (otherPattern == null) {\n                    throw new IllegalArgumentException(\n                        \"pattern [\" + patternName + \"] is referencing a non-existent pattern [\" + otherPatternName + \"]\"\n                    );\n                }\n            }\n        }\n        return patternReferences.toArray(new String[0]);\n    }\n}\n","sourceCodeStart":156,"sourceCodeEnd":190,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/grok/src/main/java/org/elasticsearch/grok/PatternBank.java#L156-L190","documentation":"Thrown while expanding pattern references during cycle detection. getPatternNamesForPattern scans each pattern for %{ tokens; if it finds a %{ with no following } or : it cannot determine the referenced name and rejects the pattern as syntactically malformed. This guards against unterminated grok substitution markers before any matching is attempted.","triggerScenarios":"Constructing a PatternBank whose map contains a value with an unterminated reference, e.g. \"FOO\": \"prefix %{BAR more text\" (no closing brace) or a lone \"%{\" in a pattern body. The parser found %{ but indexOf('}') and indexOf(':') after it both returned -1.","commonSituations":"Hand-editing a grok pattern and deleting the closing brace; a regex that legitimately needs a literal %{ without intending a substitution (must escape or restructure); truncated/copy-pasted pattern files missing the tail of a line.","solutions":["Open the named pattern shown in the message and find the %{ that has no matching } or : before the next %{ or end of string.","Close the reference (%{NAME}) or convert the literal %{ into a regex-escaped form if it was not meant as a substitution.","Validate patterns with a quick check (every %{ is followed by a } or :) before passing them to PatternBank."],"exampleFix":"// before\nMap<String,String> patterns = Map.of(\n    \"BAD\", \"time=%{TIMESTAMP ms\" // missing closing }\");\nnew PatternBank(patterns); // throws: pattern [...] has an invalid syntax\n\n// after\nMap<String,String> patterns = Map.of(\n    \"BAD\", \"time=%{TIMESTAMP:ms}\");\nnew PatternBank(patterns); // ok","handlingStrategy":"validation","validationCode":"// Reject patterns with unterminated %{ references before building the bank.\nstatic boolean syntaxOk(java.util.Map<String,String> patterns) {\n    java.util.regex.Pattern unterminated =\n        java.util.regex.Pattern.compile(\"%\\\\{[^}]*$\"); // %{ with no } before end-of-line\n    for (var e : patterns.entrySet()) {\n        if (unterminated.matcher(e.getValue()).find()) return false;\n        // also reject %{ with neither } nor : after it on the same line\n        java.util.regex.Matcher m = java.util.regex.Pattern.compile(\"%\\\\{([^}:])*\").matcher(e.getValue());\n        // (full validation mirrors PatternBank; simplest is to try-construct)\n    }\n    return true;\n}","typeGuard":null,"tryCatchPattern":"try {\n    PatternBank bank = new PatternBank(patterns);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"has an invalid syntax\")) {\n        reportConfigError(\"malformed grok pattern: \" + e.getMessage());\n    } else throw e;\n}","preventionTips":["For every %{ in a pattern, ensure a matching } or : on the same line.","Escape literal %{ as \\\\%{ if it is regex text, not a substitution.","Lint patterns in CI by constructing a PatternBank before merging catalog changes."],"tags":["grok","syntax","configuration","pattern"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T06:17:24.410Z"}