antlr/antlr4 · error · ValueError
insert op {} within boundaries of previous {}
Error message
insert op {} within boundaries of previous {} What it means
In the reduction pass, an InsertBeforeOp/InsertAfterOp whose index falls strictly inside a previous ReplaceOp's range (not exactly at its start index, which is merged as prepend) raises this ValueError. Inserting text inside a region that is about to be replaced is meaningless — the replacement would erase it — so ANTLR refuses rather than silently dropping the insert.
Source
Thrown at runtime/Python3/src/antlr4/TokenStreamRewriter.py:189
if any((iop is None, not isinstance(iop, TokenStreamRewriter.InsertBeforeOp))):
continue
prevInserts = [op for op in rewrites[:i] if isinstance(op, TokenStreamRewriter.InsertBeforeOp)]
for prev_index, prevIop in enumerate(prevInserts):
if prevIop.index == iop.index and type(prevIop) is TokenStreamRewriter.InsertBeforeOp:
iop.text += prevIop.text
rewrites[prev_index] = None
elif prevIop.index == iop.index and type(prevIop) is TokenStreamRewriter.InsertAfterOp:
iop.text = prevIop.text + iop.text
rewrites[prev_index] = None
# look for replaces where iop.index is in range; error
prevReplaces = [op for op in rewrites[:i] if isinstance(op, TokenStreamRewriter.ReplaceOp)]
for rop in prevReplaces:
if iop.index == rop.index:
rop.text = iop.text + rop.text
rewrites[i] = None
continue
if all((iop.index >= rop.index, iop.index <= rop.last_index)):
raise ValueError("insert op {} within boundaries of previous {}".format(iop, rop))
reduced = {}
for i, op in enumerate(rewrites):
if op is None: continue
if reduced.get(op.index): raise ValueError('should be only one op per index')
reduced[op.index] = op
return reduced
class RewriteOperation(object):
__slots__ = ('tokens', 'index', 'text', 'instructionIndex')
def __init__(self, tokens, index, text=""):
"""
:type tokens: CommonTokenStream
:param tokens:
:param index:
:param text:View on GitHub (pinned to 7d5770395b)
Solutions
- Move the insert outside the replaced range, or fold the inserted text into the replacement text itself.
- If the insert belongs at the start of the replaced span, use index == rop.index (that case is merged as a prepend, not an error).
- Put independent insert/replace passes in different program names and render them in separate getText() calls.
Example fix
// before rewriter.replace(2, 5, 'new') rewriter.insertBefore(3, 'x') // 3 inside 2..5 -> ValueError // after rewriter.replace(2, 5, 'xnew') # text folded into the replacement
Defensive patterns
Strategy: validation
Validate before calling
def insert_safe(replaces, index):
# replaces: list of (start, end) already submitted to this program
return all(not (start < index <= end) for start, end in replaces) Try / catch
try:
rewriter.insertBefore(i, text)
except ValueError as e:
if 'within boundaries' in str(e):
# fold text into the covering replacement instead
pass
else:
raise Prevention
- Track submitted replace ranges per program and reject/redirect inserts inside them
- Fold inserted text into the replacement string when the target index is inside a replaced span
- Keep insert and replace phases in separate programs
When it happens
Trigger: rewriter.insertAfter(3, 'x') or insertBefore(3, 'x') after rewriter.replace(2, 5, 'y') in the same program: 3 is within 2..5 and != 2, so the insert is rejected.
Common situations: Automated refactoring tools that emit inserts and replaces from independent passes; listeners that insert tokens near operators while another visitor replaces whole expressions.
Related errors
- replace: range invalid: {}..{}(size={})
- replace op boundaries of {} overlap with previous {}
- should be only one op per index
- Invalid state number.
- delegates
AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14).
Data as JSON: /api/errors/ca7cba9f337cb6ed.
Report an issue: GitHub.