Tencent/APIJSON · critical · NullPointerException
服务器内部错误,List<Join> 中 Join.onList[{}].getOriginKey() = null!
Error message
服务器内部错误,List<Join> 中 Join.onList[{}].getOriginKey() = null! What it means
Internal NullPointerException: a non-null On object in join.getOnList() returns null from getOriginKey(). The executor needs originKey (e.g. "id@/User/id") to derive the vice-table where key; null means the On was built without its origin key, breaking the JOIN cache lookup.
Source
Thrown at APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java:611
columnIndexAndJoinMap[i - 1] = curJoin;
}
// 如果是主表则直接用主表对应的 item,否则缓存副表数据到 childMap
Join prevJoin = columnIndexAndJoinMap == null || i < 2 ? null : columnIndexAndJoinMap[i - 2];
if (curJoin != prevJoin) { // 前后字段不在同一个表对象,即便后面出现 null,也不该是主表数据,而是逻辑 bug 导致
SQLConfig<T, M, L> viceConfig = curJoin != null && curJoin.isSQLJoin() ? curJoin.getCacheConfig() : null;
boolean hasPK = false;
if (viceConfig != null) { //FIXME 只有和主表关联才能用 item,否则应该从 childMap 查其它副表数据
List<On> onList = curJoin.getOnList();
int size = onList == null ? 0 : onList.size();
if (size > 0) {
String idKey = viceConfig.getIdKey();
String tblKey = config.gainTableKey();
for (int j = size - 1; j >= 0; j--) {
On on = onList.get(j);
String ok = on == null ? null : on.getOriginKey();
if (ok == null) {
throw new NullPointerException("服务器内部错误,List<Join> 中 Join.onList[" + j + (on == null ? "] = null!" : ".getOriginKey() = null!"));
}
String k = ok.substring(0, ok.length() - 1);
String ttk = on.getTargetTableKey();
M target = StringUtil.equals(ttk, tblKey) ? item : (viceItem == null ? null : JSON.get(viceItem, ttk));
Object v = target == null ? null : target.get(on.getTargetKey());
hasPK = hasPK || (k.equals(idKey) && v != null);
viceConfig.putWhere(k, v, true);
}
}
}
if (viceConfig == null) { // StringUtil.isEmpty(viceSql, true)) {
Log.i(TAG, "execute viceConfig == null >> item = null; >> ");
curItem = null;
}
View on GitHub (pinned to 5284052872)
Solutions
- Always set originKey when building On in code (it encodes key + '/' + target path).
- Check the joiner string segment corresponding to the reported index for a missing '/key@/...' part.
- Match versions of APIJSON jars; if input looks correct, file an issue with the request body.
Example fix
// before
On on = new On(); on.setKey("userId");
// after
On on = new On(); on.setKey("userId"); on.setOriginKey("userId@/User/id"); Defensive patterns
Strategy: try-catch
Validate before calling
if (join != null && join.getOnList() != null) {
for (int i = 0; i < join.getOnList().size(); i++) {
On on = join.getOnList().get(i);
if (on != null && on.getOriginKey() == null) throw new IllegalStateException("On[" + i + "] missing originKey");
}
} Try / catch
catch (NullPointerException npe) { if (npe.getMessage() != null && npe.getMessage().contains("getOriginKey() = null")) { /* rebuild join from well-formed joiner string */ } } Prevention
- Always set originKey when constructing On programmatically.
- Prefer parsed joiner strings over manual On construction.
When it happens
Trigger: Programmatically created On objects where setOriginKey was never called, or joiner-string parsing that failed to capture the origin key portion but still added the On.
Common situations: Custom extensions constructing On instances; reflective/deserialized Join configurations missing fields; version skew in On's constructor signature.
Related errors
- 服务器内部错误,List<Join> 中 Join.onList[{}] = null!
- 服务器内部错误,List<Join> 中 Join.onList[0] = null!
- 服务器内部错误,List<Join> 中 Join.onList[0].getOriginKey() = null!
- 服务器内部错误,List<Join> 中 Join.onList[0].getKey() = null!
- 服务器内部错误,解析子查询 {}/{}:{ } 为 Subquery 对象失败!
AI-assisted analysis of Tencent/APIJSON@5284052872 (2026-08-14).
Data as JSON: /api/errors/ab61012fefe3ea10.
Report an issue: GitHub.