oobabooga/textgen · warning · RuntimeError
expecting ::= at {remaining_src}
Error message
expecting ::= at {remaining_src} What it means
Raised by parse_rule (modules/grammar/grammar_utils.py:268): after reading a rule name, the very next token must be the GBNF definition operator '::=' (within whitespace). A single '=', '==', ':=', or a missing operator entirely triggers this error, echoing the offending remainder.
Source
Thrown at modules/grammar/grammar_utils.py:268
outbuf = []
remaining_src = parse_sequence(state, src, rule_name, outbuf, is_nested)
while remaining_src and remaining_src[0] == "|":
remaining_src = remove_leading_white_space(remaining_src[1:], True)
remaining_src = parse_sequence(state, remaining_src, rule_name, outbuf, is_nested)
state.grammar_encoding.append(rule_id)
state.grammar_encoding.extend(outbuf)
state.grammar_encoding.append(0)
return remaining_src
def parse_rule(state, src):
name, remaining_src = parse_name(src)
remaining_src = remove_leading_white_space(remaining_src, False)
rule_id = get_symbol_id(state, name)
if remaining_src[:3] != "::=":
raise RuntimeError("expecting ::= at " + remaining_src)
remaining_src = remove_leading_white_space(remaining_src[3:], True)
remaining_src = parse_alternates(state, remaining_src, name, rule_id, False)
if remaining_src and remaining_src[0] == "\r":
remaining_src = remaining_src[2:] if remaining_src[1] == "\n" else remaining_src[1:]
elif remaining_src and remaining_src[0] == "\n":
remaining_src = remaining_src[1:]
elif remaining_src:
raise RuntimeError("expecting newline or end at " + remaining_src)
return remove_leading_white_space(remaining_src, True)
def parse_ebnf(src):
try:
state = ParseState()
grammar_repr = remove_leading_white_space(src, True)
last_grammar_repr = ""View on GitHub (pinned to ed888c71f2)
Solutions
- Use exactly '::=' between every rule name and its body (two colons, equals).
- Look at the echoed remaining_src to see the actual operator bytes — it exposes invisible characters or typos.
- If converting from another grammar format (Lark, EBNF), script the conversion of '=' to '::='.
Example fix
// before root = "a" | "b" // after root ::= "a" | "b"
Defensive patterns
Strategy: validation
Validate before calling
import re
DEF_OP = re.compile(r'^\s*[A-Za-z0-9_-]+\s*::=')
def all_rules_use_gbnf_op(grammar: str) -> list[str]:
bad = []
for line in grammar.splitlines():
s = line.strip()
if s and not s.startswith('#') and not DEF_OP.match(line):
bad.append(s.split('::=')[0].split('=')[0].strip() or s)
return bad
Type guard
def is_gbnf_rule(line: str) -> bool:
return bool(DEF_OP.match(line)) or line.strip().startswith('#') or not line.strip()
Try / catch
try:
parse_grammar(grammar_text)
except RuntimeError as e:
if '::=' in str(e):
raise ValueError("Rules must use exactly '::=' (two colons then equals)") from None
raise
Prevention
- When porting EBNF/Lark grammars, script the '=' -> '::=' rewrite and lint the result.
- Copy the operator, don't retype it — invisible Unicode look-alikes cause this exact error.
- Keep a one-line canonical example ('root ::= "a"') at the top of your grammar files as a format anchor.
When it happens
Trigger: Grammar lines like 'root = "a"' (single '='), 'root := "a"', 'root ::= missing' handled elsewhere but 'root "a"' (operator omitted), or '::=' split by a stray comment/character.
Common situations: Writing BNF (which uses '::=' or '::='-variants like ':=='); mixing EBNF '=' style from tools like lodash-style generators or Lark grammars; typos and autocorrect mangling '::='; extra colons (':=:', '::==').
Related errors
- expecting name at {src}
- unknown escape at {src}
- unexpected end of input
- expecting ')' at {remaining_src}
- unknown hex char {c}
AI-assisted analysis of oobabooga/textgen@ed888c71f2 (2026-08-15).
Data as JSON: /api/errors/f616966ff23fa9e9.
Report an issue: GitHub.