{"record":{"id":"20e99f88291535c5","repo":"Tencent/APIJSON","slug":"list-join-join-onlist-0-null","errorCode":null,"errorMessage":"服务器内部错误，List<Join> 中 Join.onList[0] = null！","messagePattern":"服务器内部错误，List<Join> 中 Join\\.onList\\[0\\] = null！","errorType":"exception","errorClass":"NullPointerException","httpStatus":500,"severity":"critical","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java","lineNumber":782,"sourceCode":"\t\t\t\t\tLog.i(TAG, \"executeAppJoin  for (Join j : joinList) >> j.isAppJoin() == false >>  continue;\");\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tSQLConfig<T, M, L> cc = join.getCacheConfig(); //这里用config改了getSQL后再还原很麻烦，所以提前给一个config2更好\r\n\t\t\t\tif (cc == null) {\r\n\t\t\t\t\tif (Log.DEBUG) {\r\n\t\t\t\t\t\tthrow new NullPointerException(\"服务器内部错误, executeAppJoin cc == null ! 导致不能缓存 @ APP JOIN 的副表数据！\");\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tSQLConfig<T, M, L> jc = join.getJoinConfig();\r\n\r\n\t\t\t\tList<On> onList = join.getOnList();\r\n\t\t\t\tOn on = onList == null || onList.isEmpty() ? null : onList.get(0);  // APP JOIN 应该有且只有一个 ON 条件\r\n\t\t\t\tString originKey = on == null ? null : on.getOriginKey();\r\n\t\t\t\tif (originKey == null) {\r\n\t\t\t\t\tthrow new NullPointerException(\"服务器内部错误，List<Join> 中 Join.onList[0\" + (on == null ? \"] = null！\" : \".getOriginKey() = null！\"));\r\n\t\t\t\t}\r\n\t\t\t\tString key = on.getKey();\r\n\t\t\t\tif (key == null) {\r\n\t\t\t\t\tthrow new NullPointerException(\"服务器内部错误，List<Join> 中 Join.onList[0\" + (on == null ? \"] = null！\" : \".getKey() = null！\"));\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// 取出 \"id@\": \"@/User/userId\" 中所有 userId 的值\r\n\t\t\t\tList<Object> targetValueList = new ArrayList<>();\r\n\r\n\t\t\t\tfor (int i = 0; i < resultList.size(); i++) {\r\n\t\t\t\t\tM mainTable = resultList.get(i);\r\n\t\t\t\t\tObject targetValue = mainTable == null ? null : mainTable.get(on.getTargetKey());\r\n\r\n\t\t\t\t\tif (targetValue != null && targetValueList.contains(targetValue) == false) {\r\n\t\t\t\t\t\ttargetValueList.add(targetValue);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r","sourceCodeStart":764,"sourceCodeEnd":800,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java#L764-L800","documentation":"Internal NullPointerException from executeAppJoin: for an @ APP JOIN the executor requires exactly one ON condition, but onList is null/empty (or its first element is null), so on == null and originKey cannot be read. APP JOIN cannot fetch and cache vice-table rows without the ON relation.","triggerScenarios":"A \"@ APP\" type join entry whose onList was never populated — e.g. joiner string missing the key@ mapping segment, or a Join built in code without On conditions.","commonSituations":"Malformed join key like \"Comment@ User\" (no /userId@/User/id part); partial deserialization of join config; custom Join construction for APP JOIN without ON.","solutions":["Give the APP JOIN a complete relation: \"Comment/userId@/User/id\": \"@ APP\" style joiner with the /key@/Table/key mapping.","When building Join programmatically, always attach a non-empty onList with a valid first On.","If the joiner string is correct, check for version mismatch and report upstream."],"exampleFix":"// before\n\"join\": {\"Comment@\": \"@ APP\"}\n// after\n\"join\": {\"Comment/userId@/User/id\": \"@ APP\"}","handlingStrategy":"validation","validationCode":"// validate APP JOIN entry has a full relation before sending\nconst JOINER = /^[\\w$]+\\/[^/@]+@\\/?[\\w$]+\\/[^/@]+$/;\nfor (const jk of Object.keys(body['@join'] || {})) {\n  if (!JOINER.test(jk)) throw new Error('malformed join key: ' + jk);\n}","typeGuard":null,"tryCatchPattern":"try { executor.execute(config); } catch (npe) { if (npe instanceof NullPointerException && /Join\\.onList\\[0\\] = null/.test(String(npe.getMessage()))) { log.error('APP JOIN missing ON condition'); throw new BadRequestException('join needs /key@/Table/key'); } throw npe; }","preventionTips":["Always write APP JOIN as 'Table/localKey@/Target/targetKey'.","Test every join entry with a minimal request before shipping."],"tags":["apijson","app-join","internal-error","null-pointer"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}