antlr/antlr4 · error · ArgumentException
unterminated tag in pattern: {pattern}
Error message
unterminated tag in pattern: {pattern} What it means
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.
Source
Thrown at runtime/CSharp/src/Tree/Pattern/ParseTreePatternMatcher.cs:634
if (p == pattern.IndexOf(stop, p))
{
stops.Add(p);
p += stop.Length;
}
else
{
p++;
}
}
}
}
}
// System.out.println("");
// System.out.println(starts);
// System.out.println(stops);
if (starts.Count > stops.Count)
{
throw new ArgumentException("unterminated tag in pattern: " + pattern);
}
if (starts.Count < stops.Count)
{
throw new ArgumentException("missing start tag in pattern: " + pattern);
}
int ntags = starts.Count;
for (int i = 0; i < ntags; i++)
{
if (starts[i] >= stops[i])
{
throw new ArgumentException("tag delimiters out of order in pattern: " + pattern);
}
}
// collect into chunks now
if (ntags == 0)
{
string text = Sharpen.Runtime.Substring(pattern, 0, n);
chunks.Add(new TextChunk(text));View on GitHub (pinned to 7d5770395b)
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.
Example fix
// before
var p = parser.CompileParseTreePattern("x < y", RULE_expr, null);
// after
var p = parser.CompileParseTreePattern("x \< y", RULE_expr, null); Defensive patterns
Strategy: validation
Validate before calling
// Count unescaped delimiters before compiling
int 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; }
if (CountUnescaped(pattern, '<') > CountUnescaped(pattern, '>')) throw new ArgumentException("unterminated tag"); Try / catch
try { matcher.Compile(pattern); } catch (ArgumentException ex) when (ex.Message.Contains("unterminated tag")) { /* prompt user to close or escape the '<' */ } Prevention
- Escape literal angle brackets in patterns (\< and \>).
- When patterns come from users, run a delimiter-balance lint before compiling.
When it happens
Trigger: 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.
Common situations: 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 '>'.
Related errors
- missing start tag in pattern: {pattern}
- tag delimiters out of order in pattern: {pattern}
- pattern cannot be null
- start cannot be null or empty
- stop cannot be null or empty
AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14).
Data as JSON: /api/errors/06fcf4e20435eeaf.
Report an issue: GitHub.