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

  1. Ensure the joined table object stays in the request with at least the referenced alias/columns.
  2. If extending SQLConfig, verify getSQL(false) never returns empty for valid configs.
  3. Log the failing join config (table, alias, column) to identify which entry produces empty SQL; fix or remove it.
  4. 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

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


AI-assisted analysis of Tencent/APIJSON@5284052872 (2026-08-14). Data as JSON: /api/errors/93e7f5c0e6e64f46. Report an issue: GitHub.