Skip to content

NullPointerException when metadata is null during distance injection in Document mapping #4115

@little-huang

Description

@little-huang

Bug description
When mapping similarity search results to Document, the code injects a distance field into a JsonObject read from the row’s metadata. If rowRecord.get(metadataFieldName) returns null, the subsequent call to metadata.addProperty(...) throws an NPE. The code currently catches only ParamException, so a NullPointerException escapes the try/catch.

Stack trace (excerpt):

java.lang.NullPointerException: Cannot invoke "com.google.gson.JsonObject.addProperty(String, java.lang.Number)" because "metadata" is null
    at org.springframework.ai.vectorstore.milvus.MilvusVectorStore.lambda$similaritySearch$2(...)
    at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:...)
    at org.springframework.ai.vectorstore...ObservationAwareMilvusVectorStore.lambda$similaritySearch$7(...)
    at ai.shb.shbai.service.DocumentService.searchSimilarDocuments(DocumentService.java:397)
    at ai.shb.shbai.controller.DocumentController.searchDocuments(DocumentController.java:36)

Environment
Spring AI version: 1.0.0
Java version: 17.0.14
Vector store: MilvusVectorStore
Gson version: 2.13.1
Module / file / class: org.springframework.ai.vectorstore.milvus.MilvusVectorStore

Steps to reproduce

  1. Ensure a search result row where the metadata field exists but its value is null (common with legacy/custom data).

  2. Run similarity search and map rows to Document with code like:

JsonObject metadata = (JsonObject) rowRecord.get(this.metadataFieldName); // may be null
metadata.addProperty(DocumentMetadata.DISTANCE.value(), 1 - getResultSimilarity(rowRecord)); // NPE here

Expected behavior
Missing/null metadata should be handled gracefully (use an empty metadata object/map as fallback).
distance is always injected.

Minimal Complete Reproducible example
Self-contained snippet that reproduces the failure without external deps:

import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.Map;

public class MinimalRepo {

    static Map<String, Object> mapRecord(Object rawMetadata, double similarity) {
        JsonObject metadata = (JsonObject) rawMetadata; // may be null
        // This line throws when metadata == null
        metadata.addProperty("distance", 1.0 - similarity);
        Type t = new TypeToken<Map<String, Object>>(){}.getType();
        return new Gson().fromJson(metadata, t);
    }

    public static void main(String[] args) {
        System.out.println("Testing with null metadata...");
        try {
            mapRecord(null, 0.8); // -> NullPointerException
        } catch (NullPointerException e) {
            System.out.println("✓ NullPointerException caught: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions