antlr/antlr4 · error · IllegalStateException

can't alter readonly IntervalSet

Error message

can't alter readonly IntervalSet

What it means

IntervalSet guards mutation with a readonly flag: once setReadonly(true) is called, clear(), add(int), add(int,int) etc. throw IllegalStateException('can't alter readonly IntervalSet'). The runtime marks shared, immutable sets (notably sets produced during ATN deserialization and grammar-derived label sets) readonly to prevent accidental corruption of shared structures.

Source

Thrown at runtime/Java/src/org/antlr/v4/runtime/misc/IntervalSet.java:83

	}

	/** Create a set with a single element, el. */

    public static IntervalSet of(int a) {
		IntervalSet s = new IntervalSet();
        s.add(a);
        return s;
    }

    /** Create a set with all ints within range [a..b] (inclusive) */
	public static IntervalSet of(int a, int b) {
		IntervalSet s = new IntervalSet();
		s.add(a,b);
		return s;
	}

	public void clear() {
        if ( readonly ) throw new IllegalStateException("can't alter readonly IntervalSet");
		intervals.clear();
	}

    /** Add a single element to the set.  An isolated element is stored
     *  as a range el..el.
     */
    @Override
    public void add(int el) {
        if ( readonly ) throw new IllegalStateException("can't alter readonly IntervalSet");
        add(el,el);
    }

    /** Add interval; i.e., add all integers from a to b to set.
     *  If b<a, do nothing.
     *  Keep list in sorted order (by left range value).
     *  If overlap, combine ranges.  For example,
     *  If this is {1..5, 10..20}, adding 6..7 yields
     *  {1..5, 6..7, 10..20}.  Adding 4..8 yields {1..8, 10..20}.

View on GitHub (pinned to 7d5770395b)

Solutions

  1. Clone before mutating: IntervalSet copy = new IntervalSet(readonlySet); copy.add(...); (the copy starts writable)
  2. If you own the set, only call setReadonly(true) after all construction is done
  3. For 'union with existing' logic, always build a fresh writable IntervalSet and addAll from the readonly source

Example fix

// before
IntervalSet labels = transition.label(); // readonly set from ATN
labels.add(extraToken); // IllegalStateException
// after
IntervalSet labels = new IntervalSet(transition.label()); // writable copy
labels.add(extraToken);
Defensive patterns

Strategy: validation

Validate before calling

static IntervalSet writable(IntervalSet s) {
    return s.isReadonly() ? new IntervalSet(s) : s;
}
// then: writable(labels).add(tokenType);

Type guard

static boolean isMutable(IntervalSet s) { return !s.isReadonly(); }

Try / catch

catch (IllegalStateException e) { if (e.getMessage().contains("readonly")) { IntervalSet copy = new IntervalSet(original); copy.add(x); } else throw e; }

Prevention

When it happens

Trigger: Calling add/clear/remove on an IntervalSet obtained from runtime internals: transition.labelSet (SetTransition/NotSetTransition), ATNState or rule definitions from a deserialized ATN, or any set the ANTLR tool marked readonly. Also copying such a set into your own structure and then trying to mutate it without cloning.

Common situations: Building a custom error strategy or token filter that extends a label set from the ATN. Reusing IntervalSet.of(...) results vs. readonly ATN sets interchangeably. Tools that aggregate 'all tokens accepted here' by adding into an ATN-supplied set.

Related errors


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