False positive javabugs:S2259 — null checks performed by methods from external libraries are ignored

Make sure to read this post before raising a thread here:

Product: SonarQube Cloud (CI analysis via Maven)
Language: Java 25
Rule: javabugs:S2259 — A NULL object should not be accessed (“Fix this access that will throw a NullPointerException when executed.”)

Minimal reproducer

The project depends on a library jar (it could be Apache Commons Lang, Spring, Guava or any in-house utility library) that provides a classic null-safe string check:

// In an external dependency (a jar, not part of the analyzed project)
package com.example.lib;

public final class Strings {
  public static boolean hasText(CharSequence str) {
    if (str == null || str.isEmpty()) {
      return false;
    }
    for (int i = 0; i < str.length(); i++) {
      if (!Character.isWhitespace(str.charAt(i))) {
        return true;
      }
    }
    return false;
  }
}

The analyzed project uses it as a guard before dereferencing a value that may legitimately be null:

import com.example.lib.Strings;
import java.util.Locale;
import java.util.Map;

class Config {
  static String read(Map<String, String> props, String key, String defaultValue) {
    String value = props.get(key);
    return value == null ? defaultValue : value;
  }
}

class Mode {
  enum Kind { AUTO, MANUAL }

  static Kind of(Map<String, String> props) {
    String value = Config.read(props, "mode", null);
    if (!Strings.hasText(value)) {
      return Kind.AUTO;
    }
    return Kind.valueOf(value.trim().toUpperCase(Locale.ROOT)); // <-- S2259 raised here
  }
}

value can never be null when value.trim() is reached: hasText(null) always returns false.

The problem

The issue flow says “Assuming this condition to be false” on !Strings.hasText(value), i.e. it assumes hasText(null) can return true. The engine cannot see the library method, yet it keeps the “may be null” fact and follows a path that is impossible.

This “check with a library helper, then use” idiom is everywhere (StringUtils.isNotBlank, Spring StringUtils.hasText, Guava Strings.isNullOrEmpty, CollectionUtils.isNotEmpty, in-house utilities). Since S2259 is a Major Reliability bug, these false positives lower the rating and can fail the Quality Gate. This rule now raises more false positives than real bugs.

Expected behavior

  • By default, do not raise S2259 when the null path depends on the result of a method the engine cannot analyze: a null check hidden in an external library must be given the benefit of the doubt.

Hi Nicolas!

Thanks for the detailed reproducer. This comes down to a deliberate trade-off, not an oversight.

When a method call goes into code the engine can’t see (a third-party jar with no source), we don’t assume anything about its return value based on the argument, including null. That’s a conservative default: an unmodeled call is treated as fully unconstrained, rather than trusted to be null-safe.

That said, common cases are already covered: e.g. org.springframework.util.StringUtils.hasText is one of a set of well-known library methods whose semantics we understand and model directly, so it won’t raise this FP.

We deliberately don’t flip that default to “assume any unknown check makes it safe,” because there’s no rule that generalizes: some libraries treat null as “empty/invalid,” others treat it as “valid” on purpose. Giving unknown checks the benefit of the doubt would silently turn cases like that into false negatives, real NPEs the engine would stop catching.

Two questions to help us pin this down:

  • Which library/method are you actually using for the guard, if not StringUtils.hasText? If you can point us to it, we can look at adding it to the set of methods we model.
  • When did you run this analysis? We shipped support for additional null-safety library functions around September 8th, if your run was before that, it’d be worth re-running to see if this specific case is already resolved.

Best regards,

David Schroeter

● Hi David, thanks for the explanation.

Your questions

  • The method is StringUtil#hasText(CharSequence) from our own library present on the classpath as a jar.
  • The issue was raised by the SonarQube Cloud analysis of 2026-09-09, so after the September 8 rollout. We also run an internal SonarQube Server instance, so resolving it in one UI does not really solve it.

I understand that an unmodeled call must stay unconstrained. The problem is that a project has no way to close that gap for its own helpers, and every in-house hasText/isBlank utility hits the same pattern. Two suggestions:

  1. Let projects declare the contract of their own methods. The symbolic-execution plugin already ships a JSON model per library, and Spring’s StringUtils.hasText is described there as “unconstrained argument gives FALSE, NOT_NULL”. Exposing that format through an analysis property, or honoring JetBrains @Contract(“null → false”), would let teams model their own utilities without SonarSource maintaining
    anything per library.

  2. Report these cases as “may” rather than “will”. Our issue said the access will throw. The only path to it requires an unmodeled boolean method to return true for null, which the engine does not know. When the null path depends solely on the unconstrained return value of an unanalyzable method, reporting “may throw” or a lower severity would keep the detection without failing a quality gate on a guaranteed
    false positive.

For now we add an explicit value == null || before the helper call, which works but defeats the purpose of the helper.

Thanks for considering it.

Hi Nicolas!

Thanks for the follow-up and for the details.

It makes total sense that you expect SonarQube Cloud and SonarQube Server to behave the same way. The release process is much faster on Cloud, which is why the additional null-safety library models we shipped around September 8th aren’t on Server yet. They will be in the next LTA of SonarQube Server, so the gap will close soon.

Both of your suggestions are very interesting, and we will explore them. We can’t commit to a date for when either feature will be available.

One question to help us with that: you mention that the plugin already ships a JSON model per library, and that Spring’s StringUtils.hasText is described there as “unconstrained argument gives FALSE, NOT_NULL”. Could you tell us where you found it?
The specific plugin raising this rule does not ship information about library modeling in such a format, and we want to make sure we are looking at the same thing you are.

Thanks again for the good suggestions and for bringing this issue to our attention.

Best regards,

David Schroeter