antlr/antlr4 · error · Exception

pattern cannot be null

Error message

pattern cannot be null

What it means

Companion check in the same constructor: the ParseTreePattern handed to ParseTreeMatch must not be None. The pattern object is normally obtained from compiler.compile(patternText, patternRuleIndex); if that result (or a hand-built pattern) is None and is passed on, this exception is raised.

Source

Thrown at runtime/Python3/src/antlr4/tree/ParseTreeMatch.py:37

    # Constructs a new instance of {@link ParseTreeMatch} from the specified
    # parse tree and pattern.
    #
    # @param tree The parse tree to match against the pattern.
    # @param pattern The parse tree pattern.
    # @param labels A mapping from label names to collections of
    # {@link ParseTree} objects located by the tree pattern matching process.
    # @param mismatchedNode The first node which failed to match the tree
    # pattern during the matching process.
    #
    # @exception IllegalArgumentException if {@code tree} is {@code null}
    # @exception IllegalArgumentException if {@code pattern} is {@code null}
    # @exception IllegalArgumentException if {@code labels} is {@code null}
    #
    def __init__(self, tree:ParseTree, pattern:ParseTreePattern, labels:dict, mismatchedNode:ParseTree):
        if tree is None:
            raise Exception("tree cannot be null")
        if pattern is None:
            raise Exception("pattern cannot be null")
        if labels is None:
            raise Exception("labels cannot be null")
        self.tree = tree
        self.pattern = pattern
        self.labels = labels
        self.mismatchedNode = mismatchedNode

    #
    # Get the last node associated with a specific {@code label}.
    #
    # <p>For example, for pattern {@code <id:ID>}, {@code get("id")} returns the
    # node matched for that {@code ID}. If more than one node
    # matched the specified label, only the last is returned. If there is
    # no node associated with the label, this returns {@code null}.</p>
    #
    # <p>Pattern tags like {@code <ID>} and {@code <expr>} without labels are
    # considered to be labeled with {@code ID} and {@code expr}, respectively.</p>
    #

View on GitHub (pinned to 7d5770395b)

Solutions

  1. Compile the pattern once and assert it is not None before use: pattern = matcher.compile('<ID>', parser.ruleNames.index('expr')).
  2. Use dict indexing or setdefault so a missing cached pattern raises at the lookup, not deep inside match().
  3. Do not swallow exceptions from compile(); let them propagate or substitute a known-good pattern.

Example fix

# before
pattern = pattern_cache.get(rule_name)  # None on first use
m = matcher.match(tree, pattern)  # -> 'pattern cannot be null'

# after
if rule_name not in pattern_cache:
    pattern_cache[rule_name] = matcher.compile(rule_pattern_text, parser.ruleNames.index(rule_name))
m = matcher.match(tree, pattern_cache[rule_name])
Defensive patterns

Strategy: validation

Validate before calling

if pattern is None:
    pattern = matcher.compile(pattern_text, parser.ruleNames.index(rule_name))
assert pattern is not None
result = matcher.match(tree, pattern)

Type guard

def compiled_pattern(matcher, cache, name, text, parser):
    if name not in cache:
        cache[name] = matcher.compile(text, parser.ruleNames.index(name))
    return cache[name]

Prevention

When it happens

Trigger: matcher.match(tree, None), or reusing a pattern variable that failed to be created (e.g. an exception during compile was swallowed and left it None).

Common situations: Storing compiled patterns in a dict that may not contain the key (pattern = cache.get(name) returning None); swallowing compile exceptions with a bare except and continuing with a None pattern.

Related errors


AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14). Data as JSON: /api/errors/7576ae85f77516b1. Report an issue: GitHub.