jd-opensource/joyagent-jdgenie · error · RuntimeException
向量为空!
Error message
向量为空!
What it means
Parameter validation in QdrantService.upsertVector, the single-vector variant of upsertVectors: the vector argument is null, meaning there is no embedding to store for the point. The guard fails fast before constructing the PointStruct and calling Qdrant; fires when a caller attempts a single-point upsert without providing the vector data.
Solutions
- Check why the embedding step returned null; make it throw or return Optional instead of null.
- Guard the caller: skip the upsert when the vector is null and record the item for retry.
- Verify the argument order (collectionName, vector, payload) at the call site.
- Compute the embedding on demand if it was never generated, rather than upserting a null placeholder.
Example fix
// before
List<Float> vector = embeddingCache.get(text); // may be null
service.upsertVector(collection, vector, payload);
// after
List<Float> vector = Optional.ofNullable(embeddingCache.get(text))
.orElseGet(() -> embeddingClient.embed(text));
if (vector != null) {
service.upsertVector(collection, vector, payload);
} Defensive patterns
Strategy: validation
Validate before calling
if (vector == null || vector.isEmpty()) {
throw new IllegalArgumentException("vector must be computed before upsertVector");
} Type guard
static boolean hasVector(List<Float> vector) {
return vector != null && !vector.isEmpty();
} Try / catch
try {
service.upsertVector(collection, vector, payload);
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().contains("向量为空")) {
log.error("Null vector passed to upsertVector; embedding likely failed", e);
}
throw e;
} Prevention
- Make embedding clients throw or return Optional rather than null on failure
- Guard nullable embedding fields from the database before upsert
- Retry embedding generation instead of persisting a null placeholder
When it happens
Trigger: Calling upsertVector with vector == null, typically because the embedding call returned null on failure, or an unset field in the source record was passed straight through.
Common situations: Embedding client returning null instead of throwing on API failure; text preprocessing producing no output so no embedding was computed; nullable embedding column in the database; wrong variable order in the call.
Related errors
AI-assisted analysis of jd-opensource/joyagent-jdgenie@2417e0b8b6 (2026-09-08).
Data as JSON: /api/errors/7afdbfe6b7bc426c.
Report an issue: GitHub.
Appendix: source
Thrown at genie-backend/src/main/java/com/jd/genie/service/QdrantService.java:239
for (int i = 0; i < vectors.size(); i++) {
Points.PointStruct pointStruct = Points.PointStruct.newBuilder()
.setId(id(UUID.fromString(idList.get(i))))
.setVectors(vectors(vectors.get(i)))
.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();
}View on GitHub (pinned to 2417e0b8b6)