jd-opensource/joyagent-jdgenie · error · RuntimeException
元数据为空!
Error message
元数据为空!
What it means
Parameter validation in QdrantService.upsertVector: the payload (metadata map) for the single point is null, so the upsert would create a point with no metadata. This is the last of the three sequential guards (collection name, vector, payload) that validate arguments before issuing the upsertAsync call; fires when callers omit the metadata map for the vector being stored.
Solutions
- Pass an empty ImmutableMap/HashMap instead of null when no metadata exists.
- Fix the metadata source so a payload map is always produced alongside the vector.
- Guard the call site: default null payloads to Collections.emptyMap() before invoking.
- Make the metadata builder return an empty map rather than null on missing data.
Example fix
// before
service.upsertVector(collection, vector, metadata); // metadata is null
// after
Map<String, JsonWithInt.Value> payload =
metadata != null ? metadata : Collections.emptyMap();
service.upsertVector(collection, vector, payload); Defensive patterns
Strategy: validation
Validate before calling
if (payload == null) {
throw new IllegalArgumentException("payload map must not be null before upsertVector");
} Type guard
static boolean hasPayload(Map<String, ?> payload) {
return payload != null;
} Try / catch
try {
service.upsertVector(collection, vector, payload);
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().contains("元数据为空")) {
log.error("Null payload for single upsert on collection {}", collection, e);
}
throw e;
} Prevention
- Default missing metadata to Collections.emptyMap() instead of null
- Ensure metadata builders never return null; return an empty map on missing data
- Construct vector and payload together so one cannot exist without the other
When it happens
Trigger: Calling upsertVector(collection, vector, null), e.g. when metadata construction was skipped, a lookup for the document's metadata returned null, or the payload field in the source record was never populated.
Common situations: Document metadata missing in the source system; join with a metadata table returning null; nullable DTO field passed directly; refactoring that removed a default-payload builder.
Related errors
AI-assisted analysis of jd-opensource/joyagent-jdgenie@2417e0b8b6 (2026-09-08).
Data as JSON: /api/errors/fbd0606360abc408.
Report an issue: GitHub.
Appendix: source
Thrown at genie-backend/src/main/java/com/jd/genie/service/QdrantService.java:243
.putAllPayload(maps.get(i))
.build();
pointStructList.add(pointStruct);
}
return client.upsertAsync(collectionName, pointStructList).get();
}
public Points.UpdateResult upsertVector(String collectionName, List<Float> vector, Map<String, JsonWithInt.Value> payload) throws ExecutionException, InterruptedException {
if (StringUtils.isBlank(collectionName)) {
throw new RuntimeException("集合名为空!");
}
if (Objects.isNull(vector)) {
throw new RuntimeException("向量为空!");
}
if (Objects.isNull(payload)) {
throw new RuntimeException("元数据为空!");
}
List<Points.PointStruct> pointStructList = new ArrayList<>();
Points.PointStruct pointStruct = Points.PointStruct.newBuilder()
.setId(id(UUID.randomUUID()))
.setVectors(vectors(vector))
.putAllPayload(payload)
.build();
pointStructList.add(pointStruct);
return client.upsertAsync(collectionName, pointStructList).get();
}
private List<Map<String, JsonWithInt.Value>> transPayloadMap(List<Map<String, Object>> payloads) {
List<Map<String, JsonWithInt.Value>> mapList = new ArrayList<>();
View on GitHub (pinned to 2417e0b8b6)