jd-opensource/joyagent-jdgenie · error · RuntimeException
sse listener failed
Error message
sse listener failed
What it means
runNL2SQLSse streams the NL2SQL request over SSE and waits on the listener's CountDownLatch. If the Nl2SqlSseListener reports failure (isSuccess()==false), the accumulated errorMessage is wrapped in this RuntimeException. It signals the SSE stream itself failed or completed without a successful result event.
Solutions
- Inspect the appended sqlSseListener.getErrorMessage() for the stream-level cause
- Check agent logs for the requestId included in the request
- Confirm no LB/proxy idle timeout is terminating SSE connections; increase timeouts
- Retry the request; treat non-success terminal events as retryable if transient
Example fix
// before
throw new RuntimeException("sse listener failed " + sqlSseListener.getErrorMessage());
// after
throw new RuntimeException("sse listener failed: " + sqlSseListener.getErrorMessage() + ", requestId=" + request.getRequestId()); Defensive patterns
Strategy: try-catch
Try / catch
try {
return nl2SqlService.runNL2SQLSse(request, emitter);
} catch (RuntimeException e) {
if (e.getMessage().startsWith("sse listener failed")) {
log.error("sse stream failed: {}", e.getMessage());
// retry once or notify client of upstream failure
}
throw e;
} Prevention
- Configure proxy/LB SSE idle timeouts generously
- Monitor agent stability for long streams
- Implement one bounded retry for transient SSE failures
When it happens
Trigger: After await() returns, sqlSseListener.isSuccess() is false — e.g. the SSE connection failed, an error event was received, or the stream closed before a final NL2SQLResult arrived.
Common situations: Agent crash mid-stream; proxy/gateway cutting long-lived SSE connections; requestId/traceId mismatch handling; agent returning an error event for bad SQL or schema prompts.
Related errors
AI-assisted analysis of jd-opensource/joyagent-jdgenie@2417e0b8b6 (2026-09-08).
Data as JSON: /api/errors/c19e23029f026a80.
Report an issue: GitHub.
Appendix: source
Thrown at genie-backend/src/main/java/com/jd/genie/service/Nl2SqlService.java:66
request.setStream(false);
String jsonResult = OkHttpUtil.postJsonBody(dataAgentConfig.getAgentUrl() + NL2SQL_URL, null, JSONObject.toJSONString(request));
log.info("{},{} nl2sql result without sse:{}", request.getTraceId(), request.getRequestId(), jsonResult);
NL2SQLResult nl2SQLResult = JSONObject.parseObject(jsonResult, NL2SQLResult.class);
if (err.get() != null) {
throw new RuntimeException("sse nl2sql failed:" + err.get().getMessage());
}
return nl2sqlQueryData(request, nl2SQLResult);
}
public List<ChatQueryData> runNL2SQLSse(NL2SQLReq request, SseEmitter emitter) throws Exception {
AtomicReference<Throwable> err = new AtomicReference<>();
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();View on GitHub (pinned to 2417e0b8b6)