{"record":{"id":"2262b796109feed3","repo":"Tencent/APIJSON","slug":"tag-table-tag-user","errorCode":null,"errorMessage":"请在最外层传 tag ！一般是 Table 名，例如 \"tag\": \"User\" ","messagePattern":"请在最外层传 tag ！一般是 Table 名，例如 \"tag\": \"User\" ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractParser.java","lineNumber":2577,"sourceCode":"\tprotected void setRequestAttribute(String key, boolean isArray, String attrKey, @NotNull Map<String, Object> request) {\n\t\tMap<String, Object> attrMap = keyObjectAttributesMap.get(isArray ? key + KEY_ARRAY : key);\n\t\tObject attrVal = attrMap == null ? null : attrMap.get(attrKey);\n\t\tMap<String, Object> obj = attrVal == null ? null : JSON.get(request, key);\n\n\t\tif (obj != null && obj.get(attrKey) == null) {\n\t\t\t// 如果对象内部已经包含该属性,不覆盖\n\t\t\tobj.put(attrKey, attrVal);\n\t\t}\n\t}\n\n\tprotected String buildTag(Map<String, Object> request, String key, RequestMethod method, String tag) {\n\t\tif (method == RequestMethod.CRUD) {\n\t\t\tMap<String, Object> attrMap = keyObjectAttributesMap.get(key);\n\t\t\tObject _tag = attrMap == null ? null : attrMap.get(KEY_TAG);\n\t\t\treturn _tag != null ? _tag.toString() : StringUtil.isEmpty(tag) ? key : tag;\n\t\t} else {\n\t\t\tif (StringUtil.isEmpty(tag, true)) {\n\t\t\t\tthrow new IllegalArgumentException(\"请在最外层传 tag ！一般是 Table 名，例如 \\\"tag\\\": \\\"User\\\" \");\n\t\t\t}\n\t\t}\n\t\treturn tag;\n\t}\n\n\n\tprotected M objectVerify(RequestMethod method, String tag, int version, String name, @NotNull M request\n\t\t\t, int maxUpdateCount, SQLCreator<T, M, L> creator, M object) throws Exception {\n\t\t// 获取指定的JSON结构 >>>>>>>>>>>>>>\n\t\tM target = wrapRequest(method, tag, object, true);\n\t\t// Map<String, Object> clone 浅拷贝没用，Structure.parse 会导致 structure 里面被清空，第二次从缓存里取到的就是 {}\n\t\treturn getVerifier().setParser(this).verifyRequest(method, name, target, request, maxUpdateCount\n\t\t\t\t, getGlobalDatabase(), getGlobalDatasource(), getGlobalNamespace(), getGlobalCatalog(), getGlobalSchema()\n\t\t);\n\t}\n\n\t/***\n\t * 兼容url crud, 获取真实method","sourceCodeStart":2559,"sourceCodeEnd":2595,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java#L2559-L2595","documentation":"buildTag() requires a top-level \"tag\" for non-CRUD operations when the object itself carries no tag. The tag (usually the table name) selects which request structure/permission template the verifier applies; without it verification cannot proceed.","triggerScenarios":"Calling /put, /delete or another non-CRUD batch endpoint with a body that has no \"tag\": \"User\" at the top level and no per-object tag provided via the method directive map.","commonSituations":"Migrating single-object requests to batch style and forgetting the tag; frontend builds the body from form data that never included tag; tag key removed during a refactor.","solutions":["Add a top-level tag equal to the table: {\"tag\": \"User\", \"User\": {...}}","Or set a per-object tag via the method directive map: \"@put\": {\"User\": \"User\"}","Centralize tag emission in the client's request builder so every write request carries it"],"exampleFix":"// before\nPOST /put  {\"User\": {\"id\": 1, \"name\": \"a\"}}\n// after\nPOST /put  {\"tag\": \"User\", \"User\": {\"id\": 1, \"name\": \"a\"}}","handlingStrategy":"validation","validationCode":"if (method !== 'CRUD' && (req.tag == null || String(req.tag).trim() === '')) {\n  req.tag = Object.keys(req).find(k => !k.startsWith('@')) ?? null;\n  if (!req.tag) throw new Error('non-CRUD request needs a top-level tag, e.g. \"tag\":\"User\"');\n}","typeGuard":"const hasTag = req => typeof req?.tag === 'string' && req.tag.trim().length > 0;","tryCatchPattern":"try { await client.put(req); } catch (e) { if (e.message.includes('请在最外层传 tag')) { req.tag = deriveTagFromTable(req); return client.put(req); } throw e; }","preventionTips":["Request builder always injects tag = first non-@ key for write endpoints","Document tag in the API contract for every non-CRUD endpoint"],"tags":["apijson","tag","request-validation"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}