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

  1. Check why the embedding step returned null; make it throw or return Optional instead of null.
  2. Guard the caller: skip the upsert when the vector is null and record the item for retry.
  3. Verify the argument order (collectionName, vector, payload) at the call site.
  4. 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

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)