{"record":{"id":"9def5d9eff45de19","repo":"Tencent/APIJSON","slug":"errprefix-s-key-allcount","errorCode":null,"errorMessage":"{errPrefix} 中字符 '{s}' 不合法！其中 key 数量 {allCount} 已超过最大值，必须在条件键值对数量 0-{maxCombineCount} 内！","messagePattern":"(.+?) 中字符 '(.+?)' 不合法！其中 key 数量 (.+?) 已超过最大值，必须在条件键值对数量 0-(.+?) 内！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java","lineNumber":3489,"sourceCode":"\t\t\t\tboolean isBlankOrRightParenthesis = c == ' ' || c == ')';\n\t\t\t\tif (isOver || isBlankOrRightParenthesis) {\n\t\t\t\t\tboolean isEmpty = StringUtil.isEmpty(key, true);\n\t\t\t\t\tif (isEmpty && last != ')') {\n\t\t\t\t\t\tthrow new IllegalArgumentException(errPrefix + \" 中字符 '\" + (isOver ? s : s.substring(i))\n\t\t\t\t\t\t\t\t+ \"' 不合法！\" + (c == ' ' ? \"空格 ' ' \" : \"右括号 ')'\") + \" 左边缺少条件 key ！逻辑连接符 & | 左右必须各一个相邻空格！\"\n\t\t\t\t\t\t\t\t+ \"空格不能多也不能少！不允许首尾有空格，也不允许连续空格！左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格！\");\n\t\t\t\t\t}\n\n\t\t\t\t\tif (isEmpty == false) {\n\t\t\t\t\t\tif (first == false && lastLogic <= 0) {\n\t\t\t\t\t\t\tthrow new IllegalArgumentException(errPrefix + \" 中字符 \"\n\t\t\t\t\t\t\t\t\t+ \"'\" + s.substring(i - key.length() - (isOver ? 1 : 0))\n\t\t\t\t\t\t\t\t\t+ \"' 不合法！左边缺少 & | 其中一个逻辑连接符！\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tallCount ++;\n\t\t\t\t\t\tif (allCount > maxCombineCount && maxCombineCount > 0) {\n\t\t\t\t\t\t\tthrow new IllegalArgumentException(errPrefix + \" 中字符 '\" + s + \"' 不合法！\"\n\t\t\t\t\t\t\t\t\t+ \"其中 key 数量 \" + allCount + \" 已超过最大值，必须在条件键值对数量 0-\" + maxCombineCount + \" 内！\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tString column = key;\n\t\t\t\t\t\tint keyIndex = column.indexOf(\":\");\n\t\t\t\t\t\tcolumn = keyIndex > 0 ? column.substring(0, keyIndex) : column;\n\t\t\t\t\t\tObject value = conditionMap.get(column);\n\t\t\t\t\t\tString wi = \"\";\n\t\t\t\t\t\tif (value == null && conditionMap.containsKey(column) == false) { // 兼容@null\n\t\t\t\t\t\t\tisNot = false; // 以占位表达式为准\n\t\t\t\t\t\t\tsize++; // 兼容 key 数量判断\n\t\t\t\t\t\t\twi = keyIndex > 0 ? key.substring(keyIndex + 1) : \"\";\n\t\t\t\t\t\t\tif (StringUtil.isEmpty(wi)) {\n\t\t\t\t\t\t\t\tthrow new IllegalArgumentException(errPrefix + \" 中字符 '\"\n\t\t\t\t\t\t\t\t\t\t+ key + \"' 对应的条件键值对 \" + column + \":value 不存在！\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\twi = isHaving ? gainHavingItem(quote, table, alias, column, (String) value, containRaw)","sourceCodeStart":3471,"sourceCodeEnd":3507,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L3471-L3507","documentation":"A counter guard inside @combine parsing: every time a key is accepted into the expression, allCount++ is compared against maxCombineCount (getMaxCombineCount(), server-configurable). If the same expression references more keys than allowed, the request is rejected. Unlike error 147 (which caps raw condition pairs), this caps how many key references a single @combine expression may contain — it stops combinatorial explosion like \"a & b | c & d | ...\" with hundreds of terms.","triggerScenarios":"A valid but huge @combine, e.g. dynamically generated \"k1 & k2 & ... & k100\" while maxCombineCount is 5 (or whatever the deployment configured via maxCombineCount property). Each key occurrence counts, so repeated references also add up.","commonSituations":"Dynamic filter builders mapping every UI checkbox to a combine term; OR-chains over many optional search fields; strict default limits after a security-hardening upgrade.","solutions":["Reduce terms in @combine; move conditions that are always ANDed out of @combine entirely (keys not in @combine are ANDed by default).","Raise maxCombineCount in server configuration if the use case is legitimate.","Use IN-lists (\"id{}\": [...]) instead of OR-chains of individual keys.","Count terms client-side before sending (see validation code)."],"exampleFix":"// before\n{\"@combine\":\"a & b | c & d | e & f | g & h\"}\n// after (only the ORed part goes in @combine)\n{\"@combine\":\"a | c | e | g\",\"b\":1,\"d\":1,\"f\":1,\"h\":1}","handlingStrategy":"validation","validationCode":"int count = combine.split(\"[&|]\", -1).length; // each split yields a term\nif (MAX_COMBINE_COUNT > 0 && count > MAX_COMBINE_COUNT) throw new IllegalStateException(\"combine too large: \" + count);","typeGuard":"function withinCombineCount(s: string, max: number): boolean { return max <= 0 || s.split(/[&|]/).length <= max; }","tryCatchPattern":"catch (IllegalArgumentException e) { /* reduce expression terms, keep default ANDed keys outside @combine */ }","preventionTips":["Leave always-ANDed conditions out of @combine — they default to AND.","Read maxCombineCount from server config into the client's budget.","Prefer IN-lists over OR-chains."],"tags":["apijson","combine","limits","expression-parsing"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}