Tencent/APIJSON · critical · NullPointerException
AbstractSQLExecutor.executeAppJoin StringUtil.isEmpty(sql,
Error message
AbstractSQLExecutor.executeAppJoin StringUtil.isEmpty(sql, true) >> return null;
What it means
Internal NullPointerException from executeAppJoin: jc.gainSQL(false) returned an empty/blank string. The APP JOIN executor is about to run the vice-table query; an empty SQL means the join config failed to generate a statement (e.g. no table or no resolvable columns), which is a server-side invariant violation, not normal input validation.
Source
Thrown at APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java:836
// else {
// column.add("row_number()OVER(PARTITION BY " + q + key + q + " ORDER BY " + q + key + q + " ASC):_row_num_");
// }
// jc.setColumn(column);
// }
int childCount = cc.getCount();
int allChildCount = childCount*config.getCount(); // 所有分组子项数量总和
boolean isOne2Many = childCount != 1 || join.isOne2Many();
// 一对多会漏副表数据 TODO 似乎一对一走以下优化 row_number() <= childCount 逻辑也没问题
// if (isOne2Many == false && allChildCount > 0 && jc.getCount() < allChildCount) {
// jc.setCount(allChildCount);
// }
boolean prepared = jc.isPrepared();
String sql = jc.gainSQL(false);
if (StringUtil.isEmpty(sql, true)) {
throw new NullPointerException(TAG + ".executeAppJoin StringUtil.isEmpty(sql, true) >> return null;");
}
String sql2 = null;
if (childCount > 0 && isOne2Many && (jc.isMySQL() == false || jc.gainDBVersionNums()[0] >= 8)) {
// 加 row_number 字段并不会导致 count 等聚合函数统计出错,结果偏大,SQL JOIN 才会,之前没发现是因为缓存失效 bug
// boolean noAggrFun = true;
// List<String> column = jc.getColumn();
// if (column != null) {
// for (String c : column) {
// int start = c == null ? -1 : c.indexOf("(");
// int end = start <= 0 ? -1 : c.lastIndexOf(")");
// if (start > 0 && end > start) {
// String fun = c.substring(0, start);
// if (AbstractSQLConfig.SQL_AGGREGATE_FUNCTION_MAP.containsKey(fun)) {
// noAggrFun = false;
// break;
// }
// }
View on GitHub (pinned to 5284052872)
Solutions
- Ensure the joined table object stays in the request with at least the referenced alias/columns.
- If extending SQLConfig, verify getSQL(false) never returns empty for valid configs.
- Log the failing join config (table, alias, column) to identify which entry produces empty SQL; fix or remove it.
- Align APIJSON versions and check upstream issues if the request is fully standard.
Defensive patterns
Strategy: try-catch
Validate before calling
if (StringUtil.isEmpty(jc.gainSQL(false), true)) { throw new IllegalStateException('APP JOIN config produced empty SQL for table ' + jc.getTable()); } Try / catch
try { executor.execute(config); } catch (NullPointerException npe) { if (String(npe.getMessage()).contains('executeAppJoin')) { log.error('empty SQL for join config {}', config.getTable()); return serverError(); } throw npe; } Prevention
- Keep the joined table object (with table name and alias) present in every request that declares the join.
- If overriding SQLConfig.getSQL, guarantee non-empty output for valid configs.
- Log join configs before execution to catch degenerate configurations early.
When it happens
Trigger: An APP JOIN whose join config ended up with no table name or an SQL generator path returning empty — typically a misconfigured join alias referencing a table that was removed from the request, or internal state corruption after refactoring.
Common situations: Request-graph manipulation (conditionally removing tables while leaving the join), custom SQLConfig subclasses overriding getSQL/atti incorrectly, version mismatches.
Related errors
- 服务器内部错误,List<Join> 中 Join.onList[0] = null!
- 服务器内部错误,List<Join> 中 Join.onList[0].getOriginKey() = null!
- 服务器内部错误,List<Join> 中 Join.onList[0].getKey() = null!
- 服务器内部错误,解析子查询 {}/{}:{ } 为 Subquery 对象失败!
- @join:'{}' 对应的 {} 不是合法的数组 key[] !@ APP JOIN 最多允许跨 1 层,只能是子数组
AI-assisted analysis of Tencent/APIJSON@5284052872 (2026-08-14).
Data as JSON: /api/errors/93e7f5c0e6e64f46.
Report an issue: GitHub.