{"record":{"id":"a64f0dce76321bdc","repo":"Tencent/APIJSON","slug":"combine-a64f0d","errorCode":null,"errorMessage":"{}:{ @combine:'{}' } 中字符 '{}' 不合法！非逻辑符 '!' 右边多了一个字符 '{}' ！非逻辑符 '!' 右边不允许任何相邻空格 ' '，也不允许 ')' '&' '|' 中任何一个！","messagePattern":"(.+?):(.+?)' \\} 中字符 '(.+?)' 不合法！非逻辑符 '!' 右边多了一个字符 '(.+?)' ！非逻辑符 '!' 右边不允许任何相邻空格 ' '，也不允许 '\\)' '&' '\\|' 中任何一个！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java","lineNumber":3590,"sourceCode":"\t\t\t\t\t\tlastLogic = c;\n\t\t\t\t\t\ti ++;\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\tlast = i <= 0 ? 0 : s.charAt(i - 1);  // & | 后面跳过了空格\n\n\t\t\t\t\tchar next = i >= n - 1 ? 0 : s.charAt(i + 1);\n\t\t\t\t\tif (last == ' ' || last == '(') {\n\t\t\t\t\t\tif (next == ' ') {\n\t\t\t\t\t\t\tthrow new IllegalArgumentException(errPrefix + \" 中字符 '\" + s.substring(0, i + 1)\n\t\t\t\t\t\t\t+ \"' 不合法！非逻辑符 '!' 右边多了一个空格 ' ' ！非逻辑符 '!' \" +\n\t\t\t\t\t\t\t\t\t\"右边不允许任何相邻空格 ' '，也不允许 ')' '&' '|' 中任何一个！\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (next == ')' || next == '&' || next == '!') {\n\t\t\t\t\t\t\tthrow new IllegalArgumentException(errPrefix + \" 中字符 '\" + s.substring(0, i + 1)\n\t\t\t\t\t\t\t+ \"' 不合法！非逻辑符 '!' 右边多了一个字符 '\"\n\t\t\t\t\t\t\t\t\t+ next + \"' ！非逻辑符 '!' 右边不允许任何相邻空格 ' '，也不允许 ')' '&' '|' 中任何一个！\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (i > 0 && lastLogic <= 0 && last != '(') {\n\t\t\t\t\t\t\tthrow new IllegalArgumentException(errPrefix + \" 中字符 '\" + s.substring(i)\n\t\t\t\t\t\t\t+ \"' 不合法！左边缺少 & | 逻辑连接符！逻辑连接符 & | 左右必须各一个相邻空格！空格不能多也不能少！\"\n\t\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\tif (next == '(') {\n\t\t\t\t\t\tresult += SQL.NOT;\n\t\t\t\t\t\tlastLogic = c;\n\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}","sourceCodeStart":3572,"sourceCodeEnd":3608,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L3572-L3608","documentation":"When '!' is in operator position (after a space or '('), the next char must be a legal operand start. If it is ')', '&' or '!' (the check literally tests next == ')' || next == '&' || next == '!'), the NOT has nothing valid to negate — e.g. \"!)\", \"!&\", \"!!\" — and the expression is rejected, mirroring the rule that ! must be followed directly by a key or '('.","triggerScenarios":"@combine:\"id & !)name\", \"!( & a)\", \"a & !!b\" (double negation written as '!!' rather than applying ! once), or trailing \"!\" at a boundary adjacent to ')'. Trigger at line 3590. Note the message's listed forbidden set includes ')' '&' '|' but the code tests ')' '&' '!' — the message text is slightly inaccurate; the real test is those three chars.","commonSituations":"Typos (shift held too long producing '!' instead of '1'); attempting double negation '!!key'; copy-paste mangling parentheses next to NOT.","solutions":["Use a single '!' directly before a key or group: \"!name\", \"!(a | b)\".","Remove stray '!' characters; double negation is not supported — express it by restructuring the logic.","Check the char immediately after every '!' before sending (see validation code)."],"exampleFix":"// before\n{\"@combine\":\"a & !!b\"}\n// after\n{\"@combine\":\"a & b\"}","handlingStrategy":"validation","validationCode":"for (int i = 0; i < combine.length(); i++) {\n    if (combine.charAt(i) == '!' && i + 1 < combine.length()) {\n        char next = combine.charAt(i + 1);\n        if (next == ')' || next == '&' || next == '!' || next == ' ')\n            throw new IllegalArgumentException(\"invalid char after '!': \" + next);\n    }\n}","typeGuard":"function validNotFollow(s: string): boolean {\n  for (let i = 0; i < s.length; i++) {\n    if (s[i] === '!' && i + 1 < s.length) {\n      const n = s[i + 1];\n      if (n === ')' || n === '&' || n === '!' || n === ' ') return false;\n    }\n  }\n  return true;\n}","tryCatchPattern":null,"preventionTips":["One '!' only; restructure logic instead of double negation.","Ensure ')' '&' '!' never immediately follow '!'.","Watch for keyboard typos ('!' where '1' meant)."],"tags":["apijson","combine","not-operator","syntax-error"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}