{"record":{"id":"4d9d1457af055449","repo":"google/guava","slug":"should-not-call-merge-function-if-value-was-null","errorCode":null,"errorMessage":"Should not call merge function if value was null","messagePattern":"Should not call merge function if value was null","errorType":"exception","errorClass":"AssertionFailedError","httpStatus":null,"severity":"error","filePath":"android/guava-testlib/src/com/google/common/collect/testing/testers/MapMergeTester.java","lineNumber":161,"sourceCode":"                (oldV, newV) -> {\n                  assertEquals(v0(), oldV);\n                  assertEquals(v3(), newV);\n                  return null;\n                }));\n    expectMissing(e0());\n  }\n\n  public void testMergeNullValue() {\n    RuntimeException expected =\n        assertThrows(\n            RuntimeException.class,\n            () ->\n                getMap()\n                    .merge(\n                        k0(),\n                        null,\n                        (oldV, newV) -> {\n                          throw new AssertionFailedError(\n                              \"Should not call merge function if value was null\");\n                        }));\n    if (!(expected instanceof NullPointerException\n        || expected instanceof UnsupportedOperationException)) {\n      throw expected;\n    }\n  }\n\n  public void testMergeNullFunction() {\n    RuntimeException expected =\n        assertThrows(RuntimeException.class, () -> getMap().merge(k0(), v3(), null));\n    if (!(expected instanceof NullPointerException\n        || expected instanceof UnsupportedOperationException)) {\n      throw expected;\n    }\n  }\n\n  @MapFeature.Require(absent = SUPPORTS_PUT)","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/google/guava/blob/94f39958baf7ad51ddf9c70e406ed6b188194daa/android/guava-testlib/src/com/google/common/collect/testing/testers/MapMergeTester.java#L143-L179","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Validate the value parameter first: if value == null, throw NullPointerException (or UnsupportedOperationException if null values unsupported) WITHOUT calling the function.","Match the contract: null value => remove the key (if present) and return null; never invoke the function for a null value.","Ensure any exception thrown is NullPointerException or UnsupportedOperationException so the tester's expected-exception check passes."],"exampleFix":"// before\nV merged = f.apply(get(k), v); // BUG: called even when v == null\nif (merged == null) remove(k); else put(k, merged);\n\n// after\nObjects.requireNonNull(v); // NPE before touching the function\nV old = get(k);\nif (old == null) { put(k, v); return v; }\nV merged = f.apply(old, v);\nif (merged == null) remove(k); else put(k, merged);\nreturn merged;","handlingStrategy":"validation","validationCode":"RuntimeException ex = assertThrows(RuntimeException.class, () -> m.merge(k0(), null, throwingFn));\nif (!(ex instanceof NullPointerException || ex instanceof UnsupportedOperationException)) throw ex;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Null-check the value parameter at the top of merge() before invoking the function.","Throw only NullPointerException (or UnsupportedOperationException) for null values/unsupported."],"tags":["guava-testlib","map-contract","merge","null-values","assertion"],"backgroundTag":null,"analyzedSha":"94f39958baf7ad51ddf9c70e406ed6b188194daa","analyzedAt":"2026-08-13T22:50:30.265Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}