{"record":{"id":"7464a0f8b6b299fe","repo":"Tencent/APIJSON","slug":"join-e-getkey-arrkey-page-value","errorCode":null,"errorMessage":"join:'${e.getKey()}' 对应的 ${arrKey}:{ page: value } 中 value 不合法！@ APP JOIN 最多允许跨 1 层，只能是子数组，且数组对象中 page 值只能为 null 或 0 ！","messagePattern":"join:'(.+?)' 对应的 (.+?):(.+?) 中 value 不合法！@ APP JOIN 最多允许跨 1 层，只能是子数组，且数组对象中 page 值只能为 null 或 0 ！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractParser.java","lineNumber":1674,"sourceCode":"\t\t\t\ttableObj = parentPathObj == null ? null : JSON.get(parentPathObj, tableKey);\n\t\t\t\tif (tableObj == null) {\n\t\t\t\t\tthrow new NullPointerException(\"tableObj == null\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e2) {\n\t\t\t\tthrow new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + \":'\" + e.getKey() + \"' 对应的 \" + tableKey + \":value 中 value 类型不合法！\" +\n          \t\t\t\"必须是 {} 这种 Map<String, Object> 格式！\" + e2.getMessage());\n\t\t\t}\n\n\t\t\tif (arrKey != null) {\n\t\t\t\tif (parentPathObj.get(apijson.JSONRequest.KEY_JOIN) != null) {\n\t\t\t\t\tthrow new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + \":'\" + e.getKey() + \"' 对应的 \" + arrKey + \":{ join: value } 中 value 不合法！\" +\n\t\t\t\t\t\t\t\"@ APP JOIN 最多允许跨 1 层，只能是子数组，且数组对象中不能有 join: value 键值对！\");\n\t\t\t\t}\n\n\t\t\t\tInteger subPage = getInteger(parentPathObj, apijson.JSONRequest.KEY_PAGE);\n\t\t\t\tif (subPage != null && subPage != 0) {\n\t\t\t\t\tthrow new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + \":'\" + e.getKey() + \"' 对应的 \" + arrKey + \":{ page: value } 中 value 不合法！\" +\n\t\t\t\t\t\t\t\"@ APP JOIN 最多允许跨 1 层，只能是子数组，且数组对象中 page 值只能为 null 或 0 ！\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tboolean isAppJoin = \"@\".equals(joinType);\n\n\t\t\tM refObj = JSON.createJSONObject();\n\n\t\t\tString key = index < 0 ? null : path.substring(index + 1); // id@\n\t\t\tif (key != null) {  // 指定某个 key 为 JOIN ON 条件\n\t\t\t\tif (key.indexOf(\"@\") != key.length() - 1) {\n\t\t\t\t\tthrow new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + \":\" + e.getKey() + \" 中 \" + key + \" 不合法！\"\n\t\t\t\t\t\t\t+ \"必须为 &/Table0,</Table1/key1,@/Table1:alias2/key2,... 或 { '&/Table0':{}, '</Table1/key1':{},... } 这种格式！\"\n\t\t\t\t\t\t\t+ \"且 Table:alias 的 alias 必须满足英文单词变量名格式！\");\n\t\t\t\t}\n\n\t\t\t\tif (tableObj.get(key) instanceof String == false) {\n\t\t\t\t\tthrow new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + \":\" + e.getKey() + \"' 对应的 \"","sourceCodeStart":1656,"sourceCodeEnd":1692,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java#L1656-L1692","documentation":"Thrown while parsing an APP JOIN ('@/Table/key' join type) whose joined table lives inside an array wrapper (arrKey). APP JOIN is executed in application memory across at most one array level, so the wrapper object's 'page' value must be null or 0; any other page number triggers this IllegalArgumentException because server-side pagination of an APP JOIN sub-array is not supported.","triggerScenarios":"A request like { 'User[]': { 'page': 2, 'User': {...}, 'Comment': {...} }, 'join': '@/User[]/Comment/toId@' } — the array object User[] that hosts the joined table carries page: 1 or higher (page: 0 and absent page are fine).","commonSituations":"Copying a paginated multi-level array query and just changing the join type to '@'; migrating a list page that paginated both parent and child arrays; upgrading from a version where this was silently ignored.","solutions":["Delete the 'page' key (or set it to 0/null) inside the array object that contains the APP JOIN table","Paginate the joined sub-array client-side after the response returns","If server-side pagination of the child is required, split into two requests: page the main array with a normal SQL JOIN ('</Table/key@', '&/Table/key@') or query the child table separately"],"exampleFix":"// before\n{\n  'User[]': { 'page': 2, 'count': 10, 'User': {}, 'Comment': {} },\n  'join': '@/User[]/Comment/toId@'\n}\n// after\n{\n  'User[]': { 'page': 2, 'count': 10, 'User': {}, 'Comment': {} },  // page on User[] ok if join path targets User[] level... remove page from the array that hosts the join\n  'join': '@/User[]/Comment/toId@'\n}\n// simplest: drop page from the wrapper object referenced by the join path","handlingStrategy":"validation","validationCode":"function checkAppJoinPage(request) {\n  const joins = request.join;\n  if (!joins) return null;\n  const paths = typeof joins === 'string' ? [joins] : Object.keys(joins);\n  for (const p of paths) {\n    const m = p.match(/^@\\/([\\w\\[\\]]+)\\//); // '@/arrKey/Table/key@'\n    if (m && m[1].endsWith('[]')) {\n      const arr = request[m[1]];\n      if (arr && arr.page != null && arr.page !== 0) {\n        return `join ${p}: ${m[1]}.page must be 0/null, got ${arr.page}`;\n      }\n    }\n  }\n  return null;\n}","typeGuard":null,"tryCatchPattern":"try { parser.parse(request); } catch (e) { if (e instanceof IllegalArgumentException && e.getMessage().contains('page 值只能为 null 或 0')) { /* strip page and re-issue */ } else throw e; }","preventionTips":["Treat APP JOIN sub-arrays as unpaginated: never set page on the array wrapper referenced by an '@' join","Validate the request tree client-side: any object reachable from an '@' join path must have page == 0 or absent"],"tags":["apijson","join","app-join","pagination","validation"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}