google/guava · error · AssertionFailedError

Should not call merge function if key was mapped to null

Error message

Should not call merge function if key was mapped to null

What it means

Thrown inside the remapping function of MapMergeTester.testMappedToNull. Per the Map.merge contract, when the key is present but mapped to null, the function MUST NOT be invoked; merge should treat null as 'no value' and insert the new value. This firing means your Map calls the function for a null-mapped key.

Source

Thrown at android/guava-testlib/src/com/google/common/collect/testing/testers/MapMergeTester.java:76

                  throw new AssertionFailedError(
                      "Should not call merge function if key was absent");
                }));
    expectAdded(e3());
  }

  @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES})
  @CollectionSize.Require(absent = ZERO)
  public void testMappedToNull() {
    initMapWithNullValue();
    assertEquals(
        "Map.merge(keyMappedToNull, value, function) should return value",
        v3(),
        getMap()
            .merge(
                getKeyForNullValue(),
                v3(),
                (oldV, newV) -> {
                  throw new AssertionFailedError(
                      "Should not call merge function if key was mapped to null");
                }));
    expectReplacement(entry(getKeyForNullValue(), v3()));
  }

  @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS})
  public void testMergeAbsentNullKey() {
    assertEquals(
        "Map.merge(null, value, function) should return value",
        v3(),
        getMap()
            .merge(
                null,
                v3(),
                (oldV, newV) -> {
                  throw new AssertionFailedError(
                      "Should not call merge function if key was absent");
                }));

View on GitHub (pinned to 94f39958ba)

Solutions

  1. Treat a present-but-null value exactly like an absent mapping in merge: do not call the function, just store the new value.
  2. Mirror HashMap.merge semantics: oldValue == null (whether absent or explicitly null) -> store value without invoking the function.
  3. Ensure the ALLOWS_NULL_VALUES path is exercised in your own unit tests.

Example fix

// before
V old = containsKey(k) ? get(k) : DEFAULT;
V merged = f.apply(old, v); // BUG: called when get(k)==null

// after
V old = get(k);
if (old == null) { put(k, v); return v; } // null value treated as absent
V merged = f.apply(old, v);
if (merged == null) remove(k); else put(k, merged);
return merged;
Defensive patterns

Strategy: validation

Validate before calling

// Ensure null-mapped value is treated as absent.
Map<K,V> m = newMap();
m.put(k, null);
m.merge(k, v, (o,n) -> { throw new AssertionFailedError("f must not be called for null value"); });
assertEquals(v, m.get(k));

Prevention

When it happens

Trigger: Running MapTestSuiteBuilder (with ALLOWS_NULL_VALUES, size != ZERO) against your Map; testMappedToNull initializes a null value and calls merge. Your implementation calls the BiFunction instead of treating null as absent.

Common situations: A merge() that distinguishes 'contains key' from 'value non-null' incorrectly, calling apply() whenever containsKey(k) regardless of the stored null; Map implementations wrapping null values in a sentinel.

Related errors


AI-assisted analysis of google/guava@94f39958ba (2026-08-13). Data as JSON: /api/errors/9c5090af3997ca82. Report an issue: GitHub.