jd-opensource/joyagent-jdgenie · error · RuntimeException
请检查sql是否正确
Error message
请检查sql是否正确
What it means
parseSelectSql pre-validates the input with isSelectSql(sql, dialect) after cleaning the SQL and extracting hints; if the statement is not recognized as a SELECT for the given dialect, it throws this RuntimeException, meaning the SQL text is not a query the parser can process.
Solutions
- Print/inspect the cleaned SQL (log at debug shows 待解析sql) to see what actually failed the isSelectSql check
- Confirm the statement is a SELECT (or WITH/CTE select) and the dialect parameter matches the SQL flavor
- Strip markdown fences, trailing semicolons, and non-SQL text before calling parseSelectSql
- If CTE/EXPLAIN statements must be supported, extend the pre-check instead of relying on isSelectSql
Example fix
// before
parseSelectSql(userSql, dialect); // throws 请检查sql是否正确
// after
String cleaned = userSql.replaceAll("```(sql)?|;\\s*$", "").trim();
if (!cleaned.toUpperCase().startsWith("SELECT") && !cleaned.toUpperCase().startsWith("WITH")) {
throw new IllegalArgumentException("Only SELECT queries are supported");
}
parseSelectSql(cleaned, dialect); Defensive patterns
Strategy: validation
Validate before calling
String cleaned = sql == null ? "" : sql.trim().replaceFirst("^```(sql)?", "").replaceFirst("```$", "").trim();
if (cleaned.isEmpty() || !(cleaned.toUpperCase().startsWith("SELECT") || cleaned.toUpperCase().startsWith("WITH"))) {
throw new IllegalArgumentException("Only SELECT statements are supported, got: " + cleaned);
} Type guard
boolean isReadOnlySelect(String sql) {
if (sql == null) return false;
String s = sql.trim().toUpperCase();
return s.startsWith("SELECT") || s.startsWith("WITH");
} Try / catch
try {
SqlModel model = SqlParserUtils.parseSelectSql(sql, dialect);
} catch (RuntimeException e) {
if ("请检查sql是否正确".equals(e.getMessage())) {
// surface a user-facing 'please check your SQL' error
}
} Prevention
- Strip markdown fences and trailing semicolons from LLM-generated SQL before parsing
- Validate the statement starts with SELECT/WITH before calling parseSelectSql
- Pass the dialect matching the actual SQL flavor
- Reject non-SELECT statements at the API boundary
When it happens
Trigger: Calling SqlParserUtils.parseSelectSql with a non-SELECT statement (INSERT/UPDATE/DELETE/DDL/DROP), empty or null SQL after cleanSql strips it, a dialect mismatch that makes isSelectSql's keyword check fail, or a leading comment/CTE/EXPLAIN prefix the isSelectSql check does not recognize.
Common situations: Users paste a write statement into a natural-language-to-SQL chat agent; LLM-generated SQL wrapped in markdown fences or prose that cleanSql does not fully strip; CTE (WITH ...) queries that the isSelectSql heuristic does not treat as selects.
Understand the failure class
Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.
Related errors
- Invalid tool_choice: " + toolChoice
- Input must be a Map
- step_index is required for mark_step command
- 获取数据库表失败
- 获取数据库表失败
AI-assisted analysis of jd-opensource/joyagent-jdgenie@2417e0b8b6 (2026-09-08).
Data as JSON: /api/errors/10c45ea872ac1721.
Report an issue: GitHub.
Appendix: source
Thrown at genie-backend/src/main/java/com/jd/genie/data/sql/SqlParserUtils.java:396
private static String hintParse(SqlModel sqlModel, String sql) {
String regex = "(/\\*\\+)(.*?)(\\*/)";
Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
Matcher matcher = pattern.matcher(sql);
if (matcher.find()) {
sqlModel.setHint(matcher.group(2));
return matcher.replaceAll("$1" + SqlModel.HINT_TAG + "$3");
}
return sql;
}
public static SqlModel parseSelectSql(String sql, String dialect) throws SqlParseException {
log.debug("待解析sql:{}", sql);
sql = cleanSql(sql, dialect);
SqlModel sqlModel = new SqlModel();
sql = hintParse(sqlModel, sql);
if (!isSelectSql(sql, dialect)) {
throw new RuntimeException("请检查sql是否正确");
}
sqlModel.setDialect(dialect);
SqlNode sqlNode = SqlParser.create(sql, parserConfigWithoutQuoted(dialect)).parseQuery();
SqlSelect selectNode = null;
sqlModel.setSelectType(sqlNode.getKind().name());
if (SqlKind.SELECT.equals(sqlNode.getKind())) {
selectNode = (SqlSelect) sqlNode;
if (selectNode.getFetch() != null) {
sqlModel.setFetch(selectNode.getFetch().toString());
}
}
if (SqlKind.ORDER_BY.equals(sqlNode.getKind())) {
SqlOrderBy orderBy = (SqlOrderBy) sqlNode;
selectNode = (SqlSelect) orderBy.query;
List<DataOrderBy> orderByList = parseSelectOrderBy(orderBy.orderList, dialect);
sqlModel.setOrderByList(orderByList);View on GitHub (pinned to 2417e0b8b6)