{"record":{"id":"06fcf4e20435eeaf","repo":"antlr/antlr4","slug":"unterminated-tag-in-pattern-pattern-06fcf4","errorCode":null,"errorMessage":"unterminated tag in pattern: {pattern}","messagePattern":"unterminated tag in pattern: (.+?)","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"runtime/CSharp/src/Tree/Pattern/ParseTreePatternMatcher.cs","lineNumber":634,"sourceCode":"                            if (p == pattern.IndexOf(stop, p))\n                            {\n                                stops.Add(p);\n                                p += stop.Length;\n                            }\n                            else\n                            {\n                                p++;\n                            }\n                        }\n                    }\n                }\n            }\n            //\t\tSystem.out.println(\"\");\n            //\t\tSystem.out.println(starts);\n            //\t\tSystem.out.println(stops);\n            if (starts.Count > stops.Count)\n            {\n                throw new ArgumentException(\"unterminated tag in pattern: \" + pattern);\n            }\n            if (starts.Count < stops.Count)\n            {\n                throw new ArgumentException(\"missing start tag in pattern: \" + pattern);\n            }\n            int ntags = starts.Count;\n            for (int i = 0; i < ntags; i++)\n            {\n                if (starts[i] >= stops[i])\n                {\n                    throw new ArgumentException(\"tag delimiters out of order in pattern: \" + pattern);\n                }\n            }\n            // collect into chunks now\n            if (ntags == 0)\n            {\n                string text = Sharpen.Runtime.Substring(pattern, 0, n);\n                chunks.Add(new TextChunk(text));","sourceCodeStart":616,"sourceCodeEnd":652,"githubUrl":"https://github.com/antlr/antlr4/blob/7d5770395bb7b02eb56e7c62662cb1d7c08f42a3/runtime/CSharp/src/Tree/Pattern/ParseTreePatternMatcher.cs#L616-L652","documentation":"While splitting a pattern into chunks, the tag scanner found more start delimiters than stop delimiters — a '<' (or custom start) with no matching '>' before the end of the pattern. The scanner also honors the escape character (default '\\'), so an escaped '>' does not count as a stop.","triggerScenarios":"Pattern strings like \"<ID = <expr>\" or \"a < b\" (unescaped '<' used as text) passed to Compile; a custom escape via SetDelimiters that no longer matches what the pattern author used.","commonSituations":"Patterns intended to match source text containing literal angle brackets (HTML/XML/comparison operators) without escaping; user-supplied patterns from config; truncation of long patterns cutting off a closing '>'.","solutions":["Balance the delimiters: every '<' needs a matching '>' in the pattern.","Escape literal angle brackets that are text, not tags: use \\< and \\> so the scanner ignores them.","If angle brackets are common in your target text, choose different delimiters with SetDelimiters('[', ']', '\\\\').","Validate user-supplied patterns by counting unescaped starts/stops before compiling."],"exampleFix":"// before\nvar p = parser.CompileParseTreePattern(\"x < y\", RULE_expr, null);\n\n// after\nvar p = parser.CompileParseTreePattern(\"x \\< y\", RULE_expr, null);","handlingStrategy":"validation","validationCode":"// Count unescaped delimiters before compiling\nint CountUnescaped(string s, char c) { int n = 0; for (int i = 0; i < s.Length; i++) { if (s[i] == '\\\\') i++; else if (s[i] == c) n++; } return n; }\nif (CountUnescaped(pattern, '<') > CountUnescaped(pattern, '>')) throw new ArgumentException(\"unterminated tag\");","typeGuard":null,"tryCatchPattern":"try { matcher.Compile(pattern); } catch (ArgumentException ex) when (ex.Message.Contains(\"unterminated tag\")) { /* prompt user to close or escape the '<' */ }","preventionTips":["Escape literal angle brackets in patterns (\\< and \\>).","When patterns come from users, run a delimiter-balance lint before compiling."],"tags":["antlr","csharp","pattern","delimiters","escaping"],"backgroundTag":null,"analyzedSha":"7d5770395bb7b02eb56e7c62662cb1d7c08f42a3","analyzedAt":"2026-08-14T14:47:56.354Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}