{"record":{"id":"c1e164e586007afb","repo":"Tencent/APIJSON","slug":"combine-c1e164","errorCode":null,"errorMessage":"{}:{ @combine:'{}' } 中字符 '{}' 不合法！左括号 ( 比 右括号 ) 多！数量必须相等从而完整闭合 (...) ！","messagePattern":"(.+?):(.+?)' \\} 中字符 '(.+?)' 不合法！左括号 \\( 比 右括号 \\) 多！数量必须相等从而完整闭合 \\(\\.\\.\\.\\) ！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java","lineNumber":3649,"sourceCode":"\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 {\n\t\t\t\t\tkey += c;\n\t\t\t\t}\n\n\t\t\t\tlast = c;\n\t\t\t\ti ++;\n\t\t\t}\n\n\t\t\tif (depth != 0) {\n\t\t\t\tthrow new IllegalArgumentException(errPrefix + \" 中字符 '\" + s\n\t\t\t\t\t\t+ \"' 不合法！左括号 ( 比 右括号 ) 多！数量必须相等从而完整闭合 (...) ！\");\n\t\t\t}\n\t\t}\n\n\t\tList<Object> exprPreparedValues = getPreparedValueList();\n\t\tif (isHaving == false) {  // 只收集 AND 条件值\n\t\t\tsetPreparedValueList(new ArrayList<>());\n\t\t}\n\n\t\tSet<Entry<String, Object>> set = conditionMap.entrySet();\n\n\t\tString andCond = \"\";\n\t\tboolean isItemFirst = true;\n\n\t\tfor (Entry<String, Object> entry : set) {\n\t\t\tString key = entry == null ? null : entry.getKey();\n\t\t\tif (key == null || usedKeyCountMap.containsKey(key)) {\n\t\t\t\tcontinue;","sourceCodeStart":3631,"sourceCodeEnd":3667,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L3631-L3667","documentation":"Thrown after the @combine parse loop finishes: if depth != 0 there are more '(' than ')'. The whole string is reported because the imbalance is only detectable at end-of-input. Without balanced parentheses the library cannot generate a valid WHERE clause, so it rejects the request.","triggerScenarios":"@combine values like \"(a & b\" or \"((a | b) & c\" — an unclosed group at the end of the expression.","commonSituations":"Truncated strings (request size limits, copy-paste), client-side templating that omits the tail ')', or edits made to a previously working combine string.","solutions":["Count '(' and ')' in the @combine value and make them equal, closing every opened group.","Regenerate the expression with a builder that always emits matched pairs.","Re-test with a trivial \"(a & b)\" first, then add groups back one at a time."],"exampleFix":"// before\n{\"@combine\": \"(a & b | (c & d)\"}\n// after\n{\"@combine\": \"(a & b) | (c & d)\"}","handlingStrategy":"validation","validationCode":"function balanced(s) { let d = 0; for (const c of s) { if (c === '(') d++; if (c === ')') d--; } return d === 0; }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never hand-edit one end of a combine string; regenerate the whole expression.","Round-trip test: parse your combine back into groups client-side before sending."],"tags":["apijson","combine","unbalanced-parens","validation"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}