SonarQube False Positive for Nullable in Interface for NonNullApi package

Operating System: macOS Sequoia 15.7.2
SonarQube for IntelliJ plugin version: 11.6.0.83783
IntelliJ version: 2025.2.5
Programming Language: Java

SonarQube for IDE is giving a false positive for a nullability rule. I have projects where it appears as if the @NonNullApiNonNullApi annotation on the package level interferes with the @Nullable annotation on an interface in that package. For now, the only way to replicate the issue is with Spring Data’s library. I have updated to the latest versions, and the false positive report still remains.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.github.example</groupId>
	<artifactId>sonarqube-nullable-20251127</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<name>SonarQube Nullable</name>
	<description>SonarQube False Positive for &#64;Nullable in Interface for &#64;NonNullApi package.</description>

	<properties>
		<!-- Settings: maven-compiler-plugin -->
		<maven.compiler.release>25</maven.compiler.release>

		<!-- Settings: maven-resource-plugin -->
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.source.sourceEncoding>UTF-8</project.source.sourceEncoding>
		<project.report.sourceEncoding>UTF-8</project.report.sourceEncoding>

		<!-- Settings: maven-build -->
		<maven.build.timestamp.format>yyyy-MM-dd&apos;T&apos;HH:mm:ss&apos;Z&apos;</maven.build.timestamp.format>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
			<version>4.0.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-mongodb</artifactId>
			<version>5.0.0</version>
		</dependency>
	</dependencies>
</project>

package com.github.example.component;

import com.github.example.model.AttributeSource;
import com.github.example.model.OptimizedAttributes;
import com.github.example.mongo.Attribute;
import java.util.List;
import java.util.Objects;
import org.bson.Document;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import org.springframework.data.convert.PropertyValueConverter;
import org.springframework.data.convert.ValueConversionContext;

/** Converter for AttributeSource field in MongoDB documents. */
public class AttributeSourceConverter
    implements PropertyValueConverter<
        @NonNull AttributeSource, @NonNull List<Document>, @NonNull ValueConversionContext<?>> {

  /** {@inheritDoc} */
  @Override
  @Nullable
  public AttributeSource read(
      @Nullable List<Document> documents, @NonNull ValueConversionContext<?> context) {
    // Return null for null documents.
    if (documents == null) {
      return null;
    }

    // Convert to a list of attributes.
    final var attributes =
        documents.stream()
            .map(document -> context.read(document, Attribute.class))
            .filter(Objects::nonNull)
            .toList();

    // Return the attribute source.
    return new OptimizedAttributes(attributes);
  }

  /** {@inheritDoc} */
  @Override
  @Nullable
  public List<Document> write(
      @Nullable AttributeSource attributeSource, @NonNull ValueConversionContext<?> context) {
    // Return null for null attribute source.
    if (attributeSource == null) {
      return null;
    }

    // Define the attributes.
    final var attributes = attributeSource.attributes();

    // Return the list of documents.
    return attributes.stream()
        .map(attribute -> context.write(attribute, Document.class))
        .filter(Objects::nonNull)
        .toList();
  }
}

package com.github.example.model;

import com.github.example.mongo.Attribute;
import java.util.List;

public interface AttributeSource {

  List<Attribute> attributes();
}

package com.github.example.model;

import com.github.example.mongo.Attribute;
import java.util.List;

public record OptimizedAttributes(List<Attribute> attributes) implements AttributeSource {}

package com.github.example.mongo;

import static java.util.Objects.requireNonNull;

import com.github.example.component.AttributeSourceConverter;
import com.github.example.model.AttributeSource;
import com.github.example.model.OptimizedAttributes;
import java.util.List;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import org.springframework.data.convert.ValueConverter;
import org.springframework.data.mongodb.core.mapping.Field;

/**
 * Model for documents in the classification collection.
 *
 * @param id classification Id
 * @param attributes classification attribute source
 */
public record Classification(
    @Field("_id") @NonNull String id,
    @ValueConverter(AttributeSourceConverter.class) @NonNull AttributeSource attributes) {

  /**
   * The public constructor for nullable sources.
   *
   * @param id classification Id
   * @param attributes classification attribute source
   */
  public Classification(@Nullable String id, @Nullable AttributeSource attributes) {
    this.id = requireNonNull(id, "Classification Id not present.");
    this.attributes = attributes != null ? attributes : new OptimizedAttributes(List.of());
  }
}

package com.github.example.mongo;

public record Attribute(String name, String value) {}

The report is generated for the following method:
public List<Document> write( @Nullable AttributeSource attributeSource, @NonNull ValueConversionContext<?> context)

The report message:
Fix the incompatibility @NullablefNullablefNullablefNullablef the a@Nullable@Nullableotation @Nullable to honor @NullMarked at package level of the overridden method.

Hello,

I am sorry, but I was not able to reproduce this issue is the same environment as yours.
I have two hypothesis:

  1. There is something wrong with the cache of SonarQube for IDE. I suggest you (if you did not try already) to invalidate the cache of four IDE.
  2. I was not able to truly reproduce the environment you have. To address this, would you agree to create a GitHub repository with the reproducer that you shared ?

I hope we will find the cause of your issue.
Gabriel

Hello,

Yes, here is the repository:

Thank you very much for sharing the full project, I was able to identify the issue.

It is a very nice catch. I created a ticket for it :

While you wait for the fix to be applied, you can use the workaround which is to manually mark the type parameters as @NonNull.

So in the example you shared :

  /** {@inheritDoc} */
  @Override
  @Nullable
  public List<Document> write(
    ...

Becomes

  /** {@inheritDoc} */
  @Override
  @Nullable
  public List<@NonNull Document> write(
    ...

Hope this helps,
Gabriel

1 Like