Tencent/APIJSON · critical · NullPointerException
服务器内部错误,List<Join> 中 Join.onList[0].getOriginKey() = null!
Error message
服务器内部错误,List<Join> 中 Join.onList[0].getOriginKey() = null!
What it means
Internal NullPointerException from executeAppJoin: the first On of an APP JOIN exists but getOriginKey() returns null. originKey (the "key@/Table/key" string) is required to know which vice-table column maps to the main table; without it the APP JOIN data-caching step cannot proceed.
Source
Thrown at APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java:782
Log.i(TAG, "executeAppJoin for (Join j : joinList) >> j.isAppJoin() == false >> continue;");
continue;
}
SQLConfig<T, M, L> cc = join.getCacheConfig(); //这里用config改了getSQL后再还原很麻烦,所以提前给一个config2更好
if (cc == null) {
if (Log.DEBUG) {
throw new NullPointerException("服务器内部错误, executeAppJoin cc == null ! 导致不能缓存 @ APP JOIN 的副表数据!");
}
continue;
}
SQLConfig<T, M, L> jc = join.getJoinConfig();
List<On> onList = join.getOnList();
On on = onList == null || onList.isEmpty() ? null : onList.get(0); // APP JOIN 应该有且只有一个 ON 条件
String originKey = on == null ? null : on.getOriginKey();
if (originKey == null) {
throw new NullPointerException("服务器内部错误,List<Join> 中 Join.onList[0" + (on == null ? "] = null!" : ".getOriginKey() = null!"));
}
String key = on.getKey();
if (key == null) {
throw new NullPointerException("服务器内部错误,List<Join> 中 Join.onList[0" + (on == null ? "] = null!" : ".getKey() = null!"));
}
// 取出 "id@": "@/User/userId" 中所有 userId 的值
List<Object> targetValueList = new ArrayList<>();
for (int i = 0; i < resultList.size(); i++) {
M mainTable = resultList.get(i);
Object targetValue = mainTable == null ? null : mainTable.get(on.getTargetKey());
if (targetValue != null && targetValueList.contains(targetValue) == false) {
targetValueList.add(targetValue);
}
}
View on GitHub (pinned to 5284052872)
Solutions
- Set originKey on the On used for APP JOIN (full "key@/Target/key" string).
- Prefer letting APIJSON parse the joiner string rather than building On manually.
- Align jar versions; report with the request body if input is standard.
Example fix
// before
On on = new On(); on.setKey("userId"); on.setTargetKey("id");
// after
On on = new On(); on.setKey("userId"); on.setTargetKey("id"); on.setOriginKey("userId@/User/id"); Defensive patterns
Strategy: try-catch
Try / catch
catch (NullPointerException npe) { if (String(npe.getMessage()).contains("Join.onList[0].getOriginKey() = null")) { // rebuild On with full originKey 'key@/Table/key' } } Prevention
- Set originKey whenever you set key/targetKey on an On.
- Centralize On construction in one factory method that enforces all three fields.
When it happens
Trigger: An On object present in onList but constructed without originKey — typically programmatic Join/On creation or a parsing path that set key/targetTableKey but not originKey.
Common situations: Custom code extending APIJSON that builds On via setters and skips setOriginKey; migration between versions where the On model changed.
Related errors
- 服务器内部错误,List<Join> 中 Join.onList[0] = null!
- 服务器内部错误,List<Join> 中 Join.onList[0].getKey() = null!
- 服务器内部错误,List<Join> 中 Join.onList[{}] = null!
- 服务器内部错误,List<Join> 中 Join.onList[{}].getOriginKey() = null!
- AbstractSQLExecutor.executeAppJoin StringUtil.isEmpty(sql,
AI-assisted analysis of Tencent/APIJSON@5284052872 (2026-08-14).
Data as JSON: /api/errors/37efdef5b3b089fe.
Report an issue: GitHub.