oracle/graal · error · IllegalArgumentException
Cannot modify the always-empty set
Error message
Cannot modify the always-empty set
What it means
EconomicSet.createSet() with initial capacity 0 returns the shared immutable singleton EmptySet.EMPTY_SET; add(element) on it throws IllegalArgumentException('Cannot modify the always-empty set'). checkNonNull(element) runs first, so a null element throws UnsupportedOperationException instead. The singleton is immutable by design so accidental inserts fail fast.
Source
Thrown at sdk/src/org.graalvm.collections/src/org/graalvm/collections/EmptySet.java:53
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.graalvm.collections;
import java.util.Iterator;
/**
* Singleton instance for empty set. Reuses empty iterator from {@link EmptyMap}.
*/
class EmptySet {
static final EconomicSet<Object> EMPTY_SET = new EconomicSet<>() {
@Override
public boolean add(Object element) {
EconomicMapImpl.checkNonNull(element);
throw new IllegalArgumentException("Cannot modify the always-empty set");
}
@Override
public void remove(Object element) {
EconomicMapImpl.checkNonNull(element);
throw new IllegalArgumentException("Cannot modify the always-empty set");
}
@Override
public void clear() {
throw new IllegalArgumentException("Cannot modify the always-empty set");
}
@Override
public boolean contains(Object element) {
EconomicMapImpl.checkNonNull(element);
return false;
}View on GitHub (pinned to a66e9ccd1d)
Solutions
- Allocate with positive capacity — EconomicSet.createSet(1) or the no-capacity overload — whenever the set will receive elements.
- Before adding, ensure you did not capture the singleton: never store the result of createSet(0) as a mutable accumulator.
- If emptiness is data-dependent, compute capacity as Math.max(1, expectedSize).
Example fix
// before EconomicSet<T> s = EconomicSet.createSet(source.size()); // source empty -> singleton s.add(item); // throws // after EconomicSet<T> s = EconomicSet.createSet(Math.max(1, source.size())); s.add(item);
Defensive patterns
Strategy: validation
Validate before calling
EconomicSet<T> s = EconomicSet.createSet(Math.max(1, expectedSize)); // growable even when expectedSize == 0
Prevention
- Never use createSet(0) for accumulators.
- Clamp computed capacities to at least 1.
- Treat the empty singleton as read-only by contract.
When it happens
Trigger: Calling add() on the set returned by EconomicSet.createSet(0) or EconomicSet.create(EconomicSet) from an empty source — both hand back the EMPTY_SET singleton rather than a growable set.
Common situations: Creating a set sized from another collection that happens to be empty (createSet(other.size())); dedup buffers initialized lazily with capacity from a variable that computes to 0; code ported from java.util.HashSet where add on an empty set works.
Related errors
- Cannot modify the always-empty map
- Length of target array must equal the size of the set.
- Data field must be final if public
- map grown too large!
- endless collision link cycle, most likely due to unsynchroni
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/881d6828982a74b2.
Report an issue: GitHub.