{"record":{"id":"b1735e9d76b45722","repo":"Tencent/APIJSON","slug":"tag-parser-gettag-fb","errorCode":null,"errorMessage":"不允许 tag = \" + parser.getTag() + \" 的请求调用远程函数 \" + fb.getMethod() + \" ! 必须满足 tag = \" + tag + \" !","messagePattern":"不允许 tag = \" \\+ parser\\.getTag\\(\\) \\+ \" 的请求调用远程函数 \" \\+ fb\\.getMethod\\(\\) \\+ \" ! 必须满足 tag = \" \\+ tag \\+ \" !","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java","lineNumber":433,"sourceCode":"        String language = (String) row.get(\"language\");\n        String lang = \"java\".equalsIgnoreCase(language) ? null : language;\n\n        if (ENABLE_SCRIPT_FUNCTION == false && lang != null) {\n            throw new UnsupportedOperationException(\"language = \" + language + \" 不合法！AbstractFunctionParser.ENABLE_SCRIPT_FUNCTION\" +\n                    \" == false 时不支持远程函数中的脚本形式！如需支持则设置 AbstractFunctionParser.ENABLE_SCRIPT_FUNCTION = true ！\");\n        }\n\n\t\tif (lang != null && SCRIPT_EXECUTOR_MAP.get(lang) == null) {\n\t\t\tthrow new ClassNotFoundException(\"找不到脚本语言 \" + lang + \" 对应的执行引擎！请先依赖相关库并在后端 APIJSONFunctionParser<T, M, L> 中注册！\");\n\t\t}\n\n\t\tint version = row.get(\"version\") != null ? Integer.parseInt(row.get(\"version\").toString()) : 0;\n\t\tif (parser.getVersion() < version) {\n\t\t\tthrow new UnsupportedOperationException(\"不允许 version = \" + parser.getVersion() + \" 的请求调用远程函数 \" + fb.getMethod() + \" ! 必须满足 version >= \" + version + \" !\");\n\t\t}\n\t\tString tag = (String) row.get(\"tag\");  // TODO 改为 tags，类似 methods 支持多个 tag。或者干脆不要？因为目前非开放请求全都只能后端指定\n\t\tif (tag != null && tag.equals(parser.getTag()) == false) {\n\t\t\tthrow new UnsupportedOperationException(\"不允许 tag = \" + parser.getTag() + \" 的请求调用远程函数 \" + fb.getMethod() + \" ! 必须满足 tag = \" + tag + \" !\");\n\t\t}\n\t\tString[] methods = StringUtil.split((String) row.get(\"methods\"));\n\t\tList<String> ml = methods == null || methods.length <= 0 ? null : Arrays.asList(methods);\n\t\tif (ml != null && ml.contains(parser.getMethod().toString()) == false) {\n\t\t\tthrow new UnsupportedOperationException(\"不允许 method = \" + parser.getMethod() + \" 的请求调用远程函数 \" + fb.getMethod() + \" ! 必须满足 method 在 \" + Arrays.toString(methods) + \"内 !\");\n\t\t}\n\n\t\ttry {\n            return invoke(parser, fb.getMethod(), fb.getTypes(), fb.getValues(), (String) row.get(\"returnType\"), current, SCRIPT_EXECUTOR_MAP.get(lang));\n\t\t}\n        catch (Exception e) {\n\t\t\tif (e instanceof NoSuchMethodException) {\n\t\t\t\tthrow new IllegalArgumentException(\"字符 \" + function + \" 对应的远程函数 \" + getFunction(fb.getMethod(), fb.getKeys())\n                        + \" 不在后端 \" + parser.getClass().getName() + \" 内，也不在父类中！如果需要则先新增对应方法！\"\n\t\t\t\t\t\t+ \"\\n请检查函数名和参数数量是否与已定义的函数一致！\"\n\t\t\t\t\t\t+ \"\\n且必须为 function(key0,key1,...) 这种单函数格式！\"\n\t\t\t\t\t\t+ \"\\nfunction 必须符合 Java 函数命名，key 是用于在 curObj 内取值的键！\"\n\t\t\t\t\t\t+ \"\\n调用时不要有空格！\" + (Log.DEBUG ? e.getMessage() : \"\"));","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java#L415-L451","documentation":"Function rows may declare a required tag; the parser compares it with parser.getTag() (typically from the request's @tag, used to route to different database clusters/environments). A mismatch throws UnsupportedOperationException, preventing a function bound to one environment tag from being invoked by requests tagged differently.","triggerScenarios":"Function row sets tag='online' (or 'test') while the request's @tag is absent or different, so parser.getTag() does not equal the configured tag.","commonSituations":"Functions registered for the test cluster but the request omits @tag (defaults to null); promoting Function rows between environments without updating their tag; client sends the wrong tag constant.","solutions":["Send the matching tag in the request: \"@tag\": \"online\" (or whatever the Function row requires).","Or update/clear the Function row's tag column to match the environment's clients.","Audit all Function rows' tags when copying them between environments."],"exampleFix":"// before\n{\"User\":{\"@column\":\"enc(id)\"}} // row tag='online', request tag=null\n\n// after\n{\"@tag\":\"online\", \"User\":{\"@column\":\"enc(id)\"}}","handlingStrategy":"validation","validationCode":"String rowTag = (String) functionRow.get(\"tag\");\nif (rowTag != null && !rowTag.equals(requestTag)) {\n    // set \"@tag\": rowTag in the request, or clear/adjust the row's tag\n}","typeGuard":null,"tryCatchPattern":"try { parser.invoke(fn, current); } catch (UnsupportedOperationException e) { if (e.getMessage().contains(\"tag\")) { /* align request @tag with Function row */ } throw e; }","preventionTips":["Make @tag part of environment config on the client, not a per-call afterthought.","When copying Function rows across environments, re-map or clear the tag column.","Document which functions are environment-bound via tags."],"tags":["apijson","remote-function","tag","routing"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}