antlr/antlr4 · error · IllegalArgumentException

Invalid state number.

Error message

Invalid state number.

What it means

ATN.getExpectedTokens(stateNumber, context) uses stateNumber as an index into the ATN state list. A negative value or a value at least atn.states.size() cannot identify an ATN state, so the method rejects it before computing the follow set.

Source

Thrown at runtime/Java/src/org/antlr/v4/runtime/atn/ATN.java:168

	 * The big difference is that with just the input, the parser could
	 * land right in the middle of a lookahead decision. Getting
     * all *possible* tokens given a partial input stream is a separate
     * computation. See https://github.com/antlr/antlr4/issues/1428
	 *
	 * For this function, we are specifying an ATN state and call stack to compute
	 * what token(s) can come next and specifically: outside of a lookahead decision.
	 * That is what you want for error reporting and recovery upon parse error.
	 *
	 * @param stateNumber the ATN state number
	 * @param context the full parse context
	 * @return The set of potentially valid input symbols which could follow the
	 * specified state in the specified context.
	 * @throws IllegalArgumentException if the ATN does not contain a state with
	 * number {@code stateNumber}
	 */
	public IntervalSet getExpectedTokens(int stateNumber, RuleContext context) {
		if (stateNumber < 0 || stateNumber >= states.size()) {
			throw new IllegalArgumentException("Invalid state number.");
		}

		RuleContext ctx = context;
		ATNState s = states.get(stateNumber);
		IntervalSet following = nextTokens(s);
		if (!following.contains(Token.EPSILON)) {
			return following;
		}

		IntervalSet expected = new IntervalSet();
		expected.addAll(following);
		expected.remove(Token.EPSILON);
		while (ctx != null && ctx.invokingState >= 0 && following.contains(Token.EPSILON)) {
			ATNState invokingState = states.get(ctx.invokingState);
			RuleTransition rt = (RuleTransition)invokingState.transition(0);
			following = nextTokens(rt.followState);
			expected.addAll(following);
			expected.remove(Token.EPSILON);

View on GitHub (pinned to 7d5770395b)

Solutions

  1. Validate 0 <= stateNumber < atn.states.size() before the call.
  2. Prefer parser.getExpectedTokens() or RecognitionException.getExpectedTokens(), which use the recognizer's current state.
  3. Ensure the ATN, parser, context, and generated code all come from the same generated parser and ANTLR version.
  4. Treat -1 as unknown state and report a generic error instead of calling the API.

Example fix

// before
int state = ctx.getRuleIndex(); // wrong: rule index, not ATN state
IntervalSet expected = atn.getExpectedTokens(state, ctx);

// after
int state = parser.getState();
if (state >= 0 && state < atn.states.size()) {
    IntervalSet expected = atn.getExpectedTokens(state, parser.getContext());
}
Defensive patterns

Strategy: validation

Validate before calling

static boolean isValidAtnState(ATN atn, int stateNumber) {
    return stateNumber >= 0 && stateNumber < atn.states.size();
}

Try / catch

try {
    return atn.getExpectedTokens(stateNumber, context);
} catch (IllegalArgumentException e) {
    return new IntervalSet(); // or report that the state is unknown
}

Prevention

When it happens

Trigger: Passing a rule index, token type, or generated context constant where an ATN state number is expected; passing -1 from an exception whose offending state is unknown; or using a state number obtained from a different parser/ATN version.

Common situations: Custom error reporters that manually call getATN().getExpectedTokens(), mixing generated parser classes from different ANTLR builds, and code that assumes getOffendingState()/getState() is always valid.

Related errors


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