jd-opensource/joyagent-jdgenie · error · IllegalArgumentException

nl2sql返回对象为空

Error message

nl2sql返回对象为空

What it means

replaceFirstMatchedOrThrow decorates model code identifiers in the nl2sql-generated SQL with backticks. It throws IllegalArgumentException('nl2sql返回对象为空') when the input SQL or the code list is null/empty — i.e. the nl2sql result object lacked the data needed to post-process.

Solutions

  1. Ensure request.modelCodeList is populated with the model codes before calling NL2SQL
  2. Check the agent response item has a non-null nl2sql field
  3. If null input is legitimate, relax the guard to return input unchanged instead of throwing

Example fix

// before
if (input == null || codeList == null || codeList.isEmpty()) {
    throw new IllegalArgumentException("nl2sql返回对象为空");
}
// after
if (input == null) {
    return null;
}
if (codeList == null || codeList.isEmpty()) {
    return input;
}
Defensive patterns

Strategy: validation

Validate before calling

if (request.getModelCodeList() == null || request.getModelCodeList().isEmpty()) {
    throw new IllegalArgumentException("modelCodeList is required");
}
// and ensure nl2SQLData.getNl2sql() != null before prettySql

Type guard

boolean isDecoratable(String sql, List<String> codes) {
    return sql != null && codes != null && !codes.isEmpty();
}

Try / catch

try {
    pretty = prettySql(data, request);
} catch (IllegalArgumentException e) {
    log.warn("cannot decorate sql: {}", e.getMessage());
    pretty = data.getNl2sql(); // fall back to raw sql
}

Prevention

When it happens

Trigger: prettySql() calls replaceFirstMatchedOrThrow with nl2SQLData.getNl2sql()==null or request.getModelCodeList() empty/null.

Common situations: Agent returned a data item with no nl2sql field; frontend request omitted modelCodeList; older agent versions returning a different field name.

Related errors


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

Appendix: source

Thrown at genie-backend/src/main/java/com/jd/genie/service/Nl2SqlService.java:78

        Nl2SqlSseListener sqlSseListener = new Nl2SqlSseListener(emitter, request.getRequestId(), request.getTraceId());
        OkHttpUtil.requestSse(dataAgentConfig.getAgentUrl() + NL2SQL_URL, null, JSONObject.toJSONString(request), sqlSseListener);
        sqlSseListener.getCountDownLatch().await();
        int eventCount = sqlSseListener.getEventCount();
        log.info("{} sse event count:{}", request.getRequestId(), eventCount);
        if (!sqlSseListener.isSuccess()) {
            throw new RuntimeException("sse listener failed " + sqlSseListener.getErrorMessage());
        }
        NL2SQLResult nl2SQLResult = sqlSseListener.getNl2SQLResult();
        if (err.get() != null) {
            throw new RuntimeException("sse nl2sql failed:" + err.get().getMessage());
        }
        return nl2sqlQueryData(request, nl2SQLResult);
    }


    public String replaceFirstMatchedOrThrow(String input, List<String> codeList) {
        if (input == null || codeList == null || codeList.isEmpty()) {
            throw new IllegalArgumentException("nl2sql返回对象为空");
        }

        List<Pattern> patterns = codeList.stream()
                .distinct()
                .map(code -> Pattern.compile("(?i)(?<!`)\\b" + Pattern.quote(code) + "\\b(?!`)"))
                .toList();

        Matcher matcher;
        for (Pattern pattern : patterns) {
            matcher = pattern.matcher(input);
            if (matcher.find()) {
                return matcher.replaceFirst("`$0`");
            }
        }
        return input;
    }

    private List<ChatQueryData> nl2sqlQueryData(NL2SQLReq request, NL2SQLResult nl2SQLResult) throws Exception {

View on GitHub (pinned to 2417e0b8b6)