{"record":{"id":"9b5a8fa7be413049","repo":"Tencent/APIJSON","slug":"method-name-rk-table","errorCode":null,"errorMessage":"{method} 请求，{name} 里面不允许 {rk}:[] 等未定义的 Table[]:[{}] 批量操作键值对！","messagePattern":"(.+?) 请求，(.+?) 里面不允许 (.+?):\\[\\] 等未定义的 Table\\[\\]:\\[(.+?)\\] 批量操作键值对！","errorType":"validation","errorClass":"UnsupportedOperationException","httpStatus":400,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java","lineNumber":1152,"sourceCode":"\t\t\tif (rv != null && trimKeyList != null && trimKeyList.contains(rk)) {\n\t\t\t\trv = StringUtil.trim(rv);\n\t\t\t}\n\n\t\t\t// 不允许传远程函数，只能后端配置\n\t\t\tif (rk.endsWith(\"()\") && rv instanceof String) {\n\t\t\t\tthrow new UnsupportedOperationException(method + \" 请求，\" + rk + \" 不合法！\" +\n                        \"非开放请求不允许传远程函数 key():\\\"fun()\\\" ！\");\n\t\t\t}\n\n\t\t\t// 不在target内的 key:{}\n\t\t\tif (rk.startsWith(\"@\") == false && rk.endsWith(\"@\") == false && objKeySet.contains(rk) == false) {\n\t\t\t\tif (rv instanceof Map<?, ?>) {\n\t\t\t\t\tthrow new UnsupportedOperationException(method + \" 请求，\"\n                            + name + \" 里面不允许传 \" + rk + \":{} ！\");\n\t\t\t\t}\n\t\t\t\tif ((method == POST || method == PUT)\n                        && rv instanceof List<?> && isArrayKey(rk)) {\n\t\t\t\t\tthrow new UnsupportedOperationException(method + \" 请求，\" + name + \" 里面不允许 \"\n                            + rk + \":[] 等未定义的 Table[]:[{}] 批量操作键值对！\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 先让其它操作符完成\n//\t\t\tif (rv != null) { // || nulls.contains(rk)) {\n//\t\t\t\tonKeys.add(rk);\n//\t\t\t}\n\t\t}\n\t\t// 判断不允许传的key>>>>>>>>>>>>>>>>>>>>>>>>>\n\n\n\n\t\t// 校验与修改Request<<<<<<<<<<<<<<<<<\n\t\t// 在tableKeySet校验后操作，避免 导致put/add进去的Table 被当成原Request的内容\n\t\treal = operate(TYPE, type, real, parser);\n\t\treal = operate(VERIFY, verify, real, parser);\n\t\treal = operate(INSERT, insert, real, parser);","sourceCodeStart":1134,"sourceCodeEnd":1170,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java#L1134-L1170","documentation":"Thrown by AbstractVerifier.verifyRepeat/parse key-sweep when a POST or PUT request body contains a Table[] batch key (a key ending in '[]' whose value is a List, e.g. 'User[]':[{...},{...}]) inside an object whose server-side Request-table structure config does not define that key. The verifier only accepts object keys declared in the target structure (objKeySet), plus @-prefixed/suffixed reserved keys; an undeclared array key used for batch insert/update is rejected because the backend never authorized batch operations there.","triggerScenarios":"A POST/PUT request sends e.g. {\"Comment\":{...}, \"User[]\":[{\"name\":\"a\"},{\"name\":\"b\"}]} where the Request-table 'structure' config for that tag/method has no 'User[]' entry (objKeySet.contains(rk) == false, isArrayKey(rk) == true, rv instanceof List). Any POST/PUT with an undeclared 'Xxx[]':[...] pair inside a verified object triggers it.","commonSituations":"Developer adds a batch insert to a demo/front-end page but forgets to add the 'User[]':[] placeholder to the Request table structure for that method; using an existing APIJSON front-end (apijson-frontend) against a backend whose Request table was generated by an older script; copy-pasting a batch request into a tag whose structure only allows single-object operations.","solutions":["Add the batch key to the server Request table, e.g. UPDATE Request SET structure = json_set(structure, '$.User[]', json_array()) WHERE method=1 AND tag='User' (or re-run the SysTable/Request SQL script), then retry the POST/PUT.","Remove the 'Table[]':[{}] pair from the request and issue one single-object POST/PUT per row instead.","Move the batch pair into the correct top-level object that actually declares it (e.g. send it as a sibling tag declared in the structure, not nested inside another table's object).","If the key is not meant as a batch op, rename it so it does not end with '[]' or change its value from a List to a Map/scalar where appropriate."],"exampleFix":"// before (request rejected: User[] not declared in structure)\n{\"tag\":\"Moment\",\"Moment\":{\"content\":\"hi\"},\"User[]\":[{\"name\":\"a\"},{\"name\":\"b\"}]}\n\n// after: declare User[] in Request table structure for POST tag Moment, or split into single ops\n{\"tag\":\"User\",\"User\":{\"name\":\"a\"}}\n{\"tag\":\"User\",\"User\":{\"name\":\"b\"}}","handlingStrategy":"validation","validationCode":"function assertBatchKeysAllowed(requestObj, allowedKeys) {\n  for (const k of Object.keys(requestObj)) {\n    if (k.endsWith('[]') && Array.isArray(requestObj[k])\n        && !allowedKeys.includes(k)\n        && !k.startsWith('@') && !k.endsWith('@')) {\n      throw new Error(`Undeclared batch key ${k} — add it to the Request table structure or remove it`);\n    }\n  }\n}\n// run before POST/PUT:\nassertBatchKeysAllowed(body.Moment, ['User[]']);","typeGuard":"function isDeclaredBatchEntry(k, v, allowed) {\n  return typeof k === 'string' && Array.isArray(v)\n    && k.endsWith('[]') && allowed.has(k);\n}","tryCatchPattern":"try { await apijsonClient.post('/post', body); }\ncatch (e) {\n  if (/不允许.*批量操作键值对/.test(e.message)) {\n    // config gap: surface 'declare Table[] in Request structure' instead of retrying\n    throw new ConfigError('Batch key not declared for this tag: ' + e.message);\n  }\n  throw e;\n}","preventionTips":["Keep the Request table structure in version control and update it in the same commit as any client change adding a Table[] batch key.","Run a contract test per tag/method: POST the exact production body against a staging backend before release.","Reuse one shared request-builder module so batch keys are only emitted where the structure declares them."],"tags":["apijson","request-validation","batch-operations","server-config"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}