google/guava · error · AssertionFailedError

Should not call merge function if value was null

Error message

Should not call merge function if value was null

What it means

Thrown inside the remapping function of MapMergeTester.testMergeNullValue. Per Map.merge contract, if the new value is null the result must be removal of the key (or no-op if absent) and the remapping function MUST NOT be invoked. This firing means your Map incorrectly calls the function before/while handling a null new value.

Source

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

                (oldV, newV) -> {
                  assertEquals(v0(), oldV);
                  assertEquals(v3(), newV);
                  return null;
                }));
    expectMissing(e0());
  }

  public void testMergeNullValue() {
    RuntimeException expected =
        assertThrows(
            RuntimeException.class,
            () ->
                getMap()
                    .merge(
                        k0(),
                        null,
                        (oldV, newV) -> {
                          throw new AssertionFailedError(
                              "Should not call merge function if value was null");
                        }));
    if (!(expected instanceof NullPointerException
        || expected instanceof UnsupportedOperationException)) {
      throw expected;
    }
  }

  public void testMergeNullFunction() {
    RuntimeException expected =
        assertThrows(RuntimeException.class, () -> getMap().merge(k0(), v3(), null));
    if (!(expected instanceof NullPointerException
        || expected instanceof UnsupportedOperationException)) {
      throw expected;
    }
  }

  @MapFeature.Require(absent = SUPPORTS_PUT)

View on GitHub (pinned to 94f39958ba)

Solutions

  1. Validate the value parameter first: if value == null, throw NullPointerException (or UnsupportedOperationException if null values unsupported) WITHOUT calling the function.
  2. Match the contract: null value => remove the key (if present) and return null; never invoke the function for a null value.
  3. Ensure any exception thrown is NullPointerException or UnsupportedOperationException so the tester's expected-exception check passes.

Example fix

// before
V merged = f.apply(get(k), v); // BUG: called even when v == null
if (merged == null) remove(k); else put(k, merged);

// after
Objects.requireNonNull(v); // NPE before touching the function
V old = get(k);
if (old == null) { put(k, v); return v; }
V merged = f.apply(old, v);
if (merged == null) remove(k); else put(k, merged);
return merged;
Defensive patterns

Strategy: validation

Validate before calling

RuntimeException ex = assertThrows(RuntimeException.class, () -> m.merge(k0(), null, throwingFn));
if (!(ex instanceof NullPointerException || ex instanceof UnsupportedOperationException)) throw ex;

Prevention

When it happens

Trigger: testMergeNullValue passes a null value to merge and a function that throws AssertionFailedError; the wrapped exception must be NullPointerException or UnsupportedOperationException. If your Map calls the function (rethrowing the assertion) or throws something else, the test rethrows it.

Common situations: A merge() that computes merged = f.apply(old, newV) unconditionally before checking whether newV is null; implementations that throw IllegalArgumentException or a custom exception for null values instead of NPE/UnsupportedOperationException.

Related errors


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