antlr/antlr4 · error · IllegalArgumentException

replace: range invalid: {from}..{to}(size={size})

Error message

replace: range invalid: {from}..{to}(size={size})

What it means

TokenStreamRewriter.replace(programName, from, to, text) validates the token index range before queueing a ReplaceOp. It rejects from > to, from < 0, to < 0, and to >= tokens.size(), because the rewriter can only replace tokens that actually exist in the stream. The message reports the offending range and the stream size.

Source

Thrown at runtime/Java/src/org/antlr/v4/runtime/TokenStreamRewriter.java:287

	public void replace(int index, Object text) {
		replace(DEFAULT_PROGRAM_NAME, index, index, text);
	}

	public void replace(int from, int to, Object text) {
		replace(DEFAULT_PROGRAM_NAME, from, to, text);
	}

	public void replace(Token indexT, Object text) {
		replace(DEFAULT_PROGRAM_NAME, indexT, indexT, text);
	}

	public void replace(Token from, Token to, Object text) {
		replace(DEFAULT_PROGRAM_NAME, from, to, text);
	}

	public void replace(String programName, int from, int to, Object text) {
		if ( from > to || from<0 || to<0 || to >= tokens.size() ) {
			throw new IllegalArgumentException("replace: range invalid: "+from+".."+to+"(size="+tokens.size()+")");
		}
		RewriteOperation op = new ReplaceOp(from, to, text);
		List<RewriteOperation> rewrites = getProgram(programName);
		op.instructionIndex = rewrites.size();
		rewrites.add(op);
	}

	public void replace(String programName, Token from, Token to, Object text) {
		replace(programName,
				from.getTokenIndex(),
				to.getTokenIndex(),
				text);
	}

	public void delete(int index) {
		delete(DEFAULT_PROGRAM_NAME, index, index);
	}

View on GitHub (pinned to 7d5770395b)

Solutions

  1. Clamp and order the range before calling: from = Math.max(0, Math.min(from, to)); to = Math.min(to, tokens.size() - 1)
  2. Derive indexes from tokens of the same stream via token.getTokenIndex() instead of computing them manually
  3. Check for EOF/sentinel tokens (getTokenIndex() < 0) before rewriting them

Example fix

// before
rewriter.replace(ctx.start.getTokenIndex(), tokens.size(), "x"); // to >= size -> throws

// after
int from = Math.max(0, ctx.start.getTokenIndex());
int to = Math.min(ctx.stop.getTokenIndex(), tokens.size() - 1);
rewriter.replace(from, to, "x");
Defensive patterns

Strategy: validation

Validate before calling

int size = tokens.size();
if (from < 0 || to < from || to >= size) {
    throw new IllegalArgumentException("bad replace range " + from + ".." + to + " size=" + size);
}
rewriter.replace(from, to, text);

Try / catch

try { rewriter.replace(from, to, text); }
catch (IllegalArgumentException e) { /* log range, skip or clamp and retry once with corrected bounds */ }

Prevention

When it happens

Trigger: replace(from, to, ...) with from greater than to (inverted arguments); to >= tokens.size() (e.g., tokens.size() instead of tokens.size()-1); negative indexes from sentinel tokens (EOF token index can be -1) passed via from.getTokenIndex(); replace(Token indexT, text) where the token belongs to a different stream.

Common situations: Off-by-one when computing the last token index; using ctx.stop.getTokenIndex()+1 assuming another token exists; mixing tokens from a different TokenStream; EOF token (index -1) passed as the from argument.

Related errors


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