{"record":{"id":"aebd758c868e1663","repo":"Tencent/APIJSON","slug":"combine-aebd75","errorCode":null,"errorMessage":"{}:{ @combine:'{}' } 中字符 '{}' 不合法！括号 (()) 嵌套层级 {} 已超过最大值，必须在 0-{} 内！","messagePattern":"(.+?):(.+?)' \\} 中字符 '(.+?)' 不合法！括号 \\(\\(\\)\\) 嵌套层级 (.+?) 已超过最大值，必须在 0-(.+?) 内！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java","lineNumber":3622,"sourceCode":"\t\t\t\t\t}\n\t\t\t\t\telse if (last <= 0 || last == ' ' || last == '(') {\n\t\t\t\t\t\tisNot = true;\n\t\t\t\t\t\t//\t\t\t\t\tlastLogic = c;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tkey += c;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (c == '(') {\n\t\t\t\t\tif (key.isEmpty() == false || (i > 0 && lastLogic <= 0 && last != '(')) {\n\t\t\t\t\t\tthrow new IllegalArgumentException(errPrefix + \" 中字符 '\" + s.substring(i)\n\t\t\t\t\t\t+ \"' 不合法！左边缺少 & | 逻辑连接符！逻辑连接符 & | 左右必须各一个相邻空格！空格不能多也不能少！\"\n\t\t\t\t\t\t+ \"不允许首尾有空格，也不允许连续空格！左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格！\");\n\t\t\t\t\t}\n\n\t\t\t\t\tdepth ++;\n\t\t\t\t\tif (depth > maxDepth && maxDepth > 0) {\n\t\t\t\t\t\tthrow new IllegalArgumentException(errPrefix + \" 中字符 '\" + s.substring(0, i + 1)\n\t\t\t\t\t\t+ \"' 不合法！括号 (()) 嵌套层级 \" + depth + \" 已超过最大值，必须在 0-\" + maxDepth + \" 内！\");\n\t\t\t\t\t}\n\n\t\t\t\t\tresult += c;\n\t\t\t\t\tlastLogic = 0;\n\t\t\t\t\tfirst = true;\n\t\t\t\t}\n\t\t\t\telse if (c == ')') {\n\t\t\t\t\tdepth --;\n\t\t\t\t\tif (depth < 0) {\n\t\t\t\t\t\tthrow new IllegalArgumentException(errPrefix + \" 中字符 '\" + s.substring(0, i + 1)\n\t\t\t\t\t\t+ \"' 不合法！左括号 ( 比 右括号 ) 少！数量必须相等从而完整闭合 (...) ！\");\n\t\t\t\t\t}\n\n\t\t\t\t\tresult += c;\n\t\t\t\t\tlastLogic = 0;\n\t\t\t\t}\n\t\t\t\telse {","sourceCodeStart":3604,"sourceCodeEnd":3640,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L3604-L3640","documentation":"Thrown while parsing @combine when the parenthesis nesting depth exceeds the configured maximum (maxDepth). Each '(' increments depth; if depth becomes greater than maxDepth (and maxDepth > 0) the parser stops and rejects the expression to bound recursion/complexity of the generated WHERE clause.","triggerScenarios":"A @combine value with more than the allowed nested parentheses, e.g. \"((a & b) | (c & (d | e)))\" when maxDepth is limited to 2 or the default MAX_QUERY_DEPTH.","commonSituations":"Complex multi-group filtering built dynamically by a frontend that nests groups deeper than the server allows; default depth limits changing between APIJSON versions.","solutions":["Flatten the @combine expression to fewer nesting levels (use distributive rewriting like a&(b|c) => (a&b)|(a&c) only when needed; usually just reduce grouping).","Raise the max query depth configuration if your deployment genuinely needs deeper nesting.","Move deep grouping logic into a server-side custom API or view instead of @combine."],"exampleFix":"// before\n{\"@combine\": \"((a & b) | (c & (d | e))) & f\"}\n// after\n{\"@combine\": \"(a & b) | (c & d) | (c & e)\"}","handlingStrategy":"validation","validationCode":"function combineDepth(s) {\n  let d = 0, max = 0;\n  for (const ch of s) { if (ch === '(') max = Math.max(max, ++d); if (ch === ')') d--; }\n  return max;\n}\n// reject before sending\nif (combineDepth(combineStr) > 2) throw new Error('combine nesting too deep');","typeGuard":null,"tryCatchPattern":"try { await apijson.put(req); } catch (e) { if (/嵌套层级/.test(e.message)) flattenCombine(); else throw e; }","preventionTips":["Cap nesting at 2 levels in UI query builders.","Check depth client-side with a parenthesis counter before each request.","Prefer splitting very complex filtering into multiple requests or a custom API."],"tags":["apijson","combine","nesting-limit","validation"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}