oracle/graal · error · IllegalArgumentException

Cannot modify the always-empty map

Error message

Cannot modify the always-empty map

What it means

EconomicMap.createMap() may return the immutable always-empty singleton EmptyMap.EMPTY_MAP when an initial capacity of 0 is requested. Any structural mutation — put(key, value) — throws IllegalArgumentException('Cannot modify the always-empty map'). The singleton is deliberately immutable so accidental writes fail fast instead of silently dropping data. Note checkNonNull(key) runs first, so a null key on an empty map throws UnsupportedOperationException instead.

Source

Thrown at sdk/src/org.graalvm.collections/src/org/graalvm/collections/EmptyMap.java:102

        @Override
        public Object next() {
            throw new NoSuchElementException("Empty iterator does not have elements");
        }
    };

    static final Iterable<Object> EMPTY_ITERABLE = new Iterable<>() {
        @Override
        public Iterator<Object> iterator() {
            return EMPTY_ITERATOR;
        }
    };

    static final EconomicMap<Object, Object> EMPTY_MAP = new EconomicMap<>() {
        @Override
        public Object put(Object key, Object value) {
            EconomicMapImpl.checkNonNull(key);
            throw new IllegalArgumentException("Cannot modify the always-empty map");
        }

        @Override
        public void clear() {
            throw new IllegalArgumentException("Cannot modify the always-empty map");
        }

        @Override
        public Object removeKey(Object key) {
            EconomicMapImpl.checkNonNull(key);
            throw new IllegalArgumentException("Cannot modify the always-empty map");
        }

        @Override
        public Object get(Object key) {
            EconomicMapImpl.checkNonNull(key);
            return null;
        }

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Create the map with a positive initial capacity (e.g. EconomicMap.createMap(1)) when you intend to insert, or use the no-capacity overload createMap().
  2. Before putting, test map == EconomicMap.createMap(0) style singletons indirectly by always allocating growable maps for mutable use.
  3. If a map may stay empty but is later mutated, never request capacity 0.

Example fix

// before
EconomicMap<K,V> m = EconomicMap.createMap(src.size()); // src empty -> capacity 0 -> singleton
m.put(k, v); // throws

// after
EconomicMap<K,V> m = EconomicMap.createMap(Math.max(1, src.size()));
m.put(k, v);
Defensive patterns

Strategy: validation

Validate before calling

EconomicMap<K,V> m = EconomicMap.createMap(Math.max(1, expectedSize)); // never capacity 0 for mutable use

Prevention

When it happens

Trigger: Calling put() on the map returned by EconomicMap.createMap(0) (or EconomicMap.create(EconomicMapUtil) with zero capacity) — this returns the shared EMPTY_MAP singleton, not a growable instance.

Common situations: Sizing maps from a variable that computes to 0 (e.g. createMap(other.size()) when other is empty); 'clone-then-populate' patterns that start from an empty copy; versions of code that previously used HashMap and tolerated put on a zero-sized map.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/b15f848a9dd5a720. Report an issue: GitHub.