{"record":{"id":"ef8dfa5d3060b00f","repo":"Tencent/APIJSON","slug":"list-join-join-onlist-null","errorCode":null,"errorMessage":"服务器内部错误，List<Join> 中 Join.onList[{}] = null！","messagePattern":"服务器内部错误，List<Join> 中 Join\\.onList\\[(.+?)\\] = null！","errorType":"exception","errorClass":"NullPointerException","httpStatus":500,"severity":"critical","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java","lineNumber":611,"sourceCode":"\t\t\t\t\t\t\tcolumnIndexAndJoinMap[i - 1] = curJoin;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t// 如果是主表则直接用主表对应的 item，否则缓存副表数据到 childMap\r\n\t\t\t\t\t\tJoin prevJoin = columnIndexAndJoinMap == null || i < 2 ? null : columnIndexAndJoinMap[i - 2];\r\n\t\t\t\t\t\tif (curJoin != prevJoin) {  // 前后字段不在同一个表对象，即便后面出现 null，也不该是主表数据，而是逻辑 bug 导致\r\n\t\t\t\t\t\t\tSQLConfig<T, M, L> viceConfig = curJoin != null && curJoin.isSQLJoin() ? curJoin.getCacheConfig() : null;\r\n\t\t\t\t\t\t\tboolean hasPK = false;\r\n\t\t\t\t\t\t\tif (viceConfig != null) {  //FIXME 只有和主表关联才能用 item，否则应该从 childMap 查其它副表数据\r\n\t\t\t\t\t\t\t\tList<On> onList = curJoin.getOnList();\r\n\t\t\t\t\t\t\t\tint size = onList == null ? 0 : onList.size();\r\n\t\t\t\t\t\t\t\tif (size > 0) {\r\n\t\t\t\t\t\t\t\t\tString idKey = viceConfig.getIdKey();\r\n\t\t\t\t\t\t\t\t\tString tblKey = config.gainTableKey();\r\n\t\t\t\t\t\t\t\t\tfor (int j = size - 1; j >= 0; j--) {\r\n\t\t\t\t\t\t\t\t\t\tOn on = onList.get(j);\r\n\t\t\t\t\t\t\t\t\t\tString ok = on == null ? null : on.getOriginKey();\r\n\t\t\t\t\t\t\t\t\t\tif (ok == null) {\r\n\t\t\t\t\t\t\t\t\t\t\tthrow new NullPointerException(\"服务器内部错误，List<Join> 中 Join.onList[\" + j + (on == null ? \"] = null！\" : \".getOriginKey() = null！\"));\r\n\t\t\t\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\t\t\t\tString k = ok.substring(0, ok.length() - 1);\r\n\t\t\t\t\t\t\t\t\t\tString ttk = on.getTargetTableKey();\r\n\r\n\t\t\t\t\t\t\t\t\t\tM target = StringUtil.equals(ttk, tblKey) ? item : (viceItem == null ? null : JSON.get(viceItem, ttk));\r\n\t\t\t\t\t\t\t\t\t\tObject v = target == null ? null : target.get(on.getTargetKey());\r\n\t\t\t\t\t\t\t\t\t\thasPK = hasPK || (k.equals(idKey) && v != null);\r\n\r\n\t\t\t\t\t\t\t\t\t\tviceConfig.putWhere(k, v, true);\r\n\t\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\tif (viceConfig == null) { // StringUtil.isEmpty(viceSql, true)) {\r\n\t\t\t\t\t\t\t\tLog.i(TAG, \"execute viceConfig == null >> item = null; >> \");\r\n\t\t\t\t\t\t\t\tcurItem = null;\r\n\t\t\t\t\t\t\t}\r","sourceCodeStart":593,"sourceCodeEnd":629,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java#L593-L629","documentation":"Internal NullPointerException from the JOIN-cache loop in the SQL executor: while back-filling vice-table cache configs from join ON conditions, an element of join.getOnList() is null at index j. This is a server-side data-structure invariant violation, not a user-input validation error.","triggerScenarios":"A JOIN (typically APP JOIN on cached data) whose onList contains a null entry — usually from programmatic construction of Join/On objects or a partial deserialization of a joiner string.","commonSituations":"Building Join objects manually in extended code; version mismatches between APIJSON modules producing incompletely populated Join instances; malformed \"Table/key@/Target/key\" joiner strings that parse into sparse lists.","solutions":["Fix the joiner syntax so every ON entry parses fully: \"Table/alias/key@/Target/alias2/key\".","If constructing Join objects in code, never add null On entries; validate before adding.","Align APIJSON artifact versions (ORM vs framework) across the classpath.","Report with full request body if the joiner string itself looks correct — it may be an internal bug."],"exampleFix":"// before\njoin.setOnList(Arrays.asList(on, null));\n// after\njoin.setOnList(Collections.singletonList(on));","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { result = executor.execute(config); } catch (npe) { if (npe instanceof NullPointerException && /Join\\.onList\\[\\d+\\] = null/.test(npe.message)) { log.error('join onList corrupt for {}', config.getTable(), npe); return serverError(); } throw npe; }","preventionTips":["Do not hand-construct Join/On lists; use the joiner-string parser.","Filter null On entries before setOnList in extension code.","Keep all APIJSON modules at one version."],"tags":["apijson","join","internal-error","null-pointer"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}