jd-opensource/joyagent-jdgenie · error · RuntimeException

dataList is null!

Error message

dataList is null!

What it means

Validation in VectorService.saveVector: the dataList field of the VectorSaveReq (the vectors/metadata to persist) is null, so there is nothing to save. The method wraps its work in a broad try-catch that logs and rethrows as RuntimeException, so this input check is the fast-fail path before any Qdrant interaction; fires when callers submit a save request with an absent or empty data list.

Solutions

  1. Check dataList.size() > 0 at the caller before invoking saveVector and skip the call for empty batches.
  2. Investigate why the upstream pipeline produced zero records (source query, filters).
  3. If empty batches are legitimate, treat them as a no-op in the caller instead of an error.
  4. Ensure each VectorData in dataList has a non-blank embeddingText, since blank texts will produce useless embeddings.

Example fix

// before
vectorService.saveVector(buildSaveReq(records)); // records may be empty
// after
List<VectorSaveReq.VectorData> items = toVectorData(records);
if (items.isEmpty()) {
    log.info("no records to save, skipping saveVector");
    return true;
}
vectorService.saveVector(buildSaveReq(items));
Defensive patterns

Strategy: validation

Validate before calling

// java (caller)
if (saveReq.getDataList() == null || saveReq.getDataList().isEmpty()) {
    return true; // nothing to save
}

Type guard

boolean canSave(VectorSaveReq req) {
    return req != null && CollectionUtils.isNotEmpty(req.getDataList());
}

Try / catch

try {
    vectorService.saveVector(saveReq);
} catch (RuntimeException e) {
    if (e.getMessage() != null && e.getMessage().contains("dataList is null")) {
        log.warn("saveVector called with empty dataList — check upstream pipeline");
        return false;
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling saveVector with dataList == null or an empty list — typically an upstream batch/transform step produced zero records (e.g., source query returned nothing) and the result was passed through unchanged.

Common situations: Upstream data extraction returned no rows for the sync window; a filter step removed all items before save; caller misuses the API assuming saveVector handles empty batches as a no-op.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of jd-opensource/joyagent-jdgenie@2417e0b8b6 (2026-09-08). Data as JSON: /api/errors/c45a2b4435ce1870. Report an issue: GitHub.

Appendix: source

Thrown at genie-backend/src/main/java/com/jd/genie/service/VectorService.java:131

                Map<String, JsonWithInt.Value> payloadMap = p.getPayloadMap();
                payloadMap.forEach((k, v) -> hashMap.put(k, v.getStringValue()));
                hashMap.put("score", p.getScore());
                hashMap.put("_id", p.getId().getUuid());
                return hashMap;
            }).collect(Collectors.toList());
        } catch (Exception e) {
            log.error("vectorRecall error: req:{}", JSONObject.toJSONString(req), e);
            throw new RuntimeException(e);
        }
    }

    public Boolean saveVector(VectorSaveReq vectorSaveReq) {
        try {
            if (StringUtils.isBlank(vectorSaveReq.getCollectionName())) {
                throw new RuntimeException("collectionName is null!");
            }
            if (CollectionUtils.isEmpty(vectorSaveReq.getDataList())) {
                throw new RuntimeException("dataList is null!");
            }

            List<String> textList = vectorSaveReq.getDataList().stream().map(VectorSaveReq.VectorData::getEmbeddingText).collect(Collectors.toList());
            List<List<Float>> vector = embeddingService.getVectorBatch(textList);
            List<String> idList = vectorSaveReq.getDataList().stream().map(data -> {
                if (StringUtils.isNotBlank(data.getUuid()) && isUuid(data.getUuid())) {
                    return data.getUuid();
                }
                return UUID.randomUUID().toString();
            }).collect(Collectors.toList());
            List<Map<String, Object>> payloads = vectorSaveReq.getDataList().stream().map(VectorSaveReq.VectorData::getPayloads).collect(Collectors.toList());
            qdrantService.upsertVectorsPayloadTrans(vectorSaveReq.getCollectionName(), idList, vector, payloads);
            return true;
        } catch (Exception e) {
            log.error("saveVector error: req:{}", JSONObject.toJSONString(vectorSaveReq), e);
            return false;
        }
    }

View on GitHub (pinned to 2417e0b8b6)