Tencent/APIJSON · error · IllegalArgumentException

字符 {expression} 不合法!{key}:value 中 value 里的 SQL函数必须为 function

Error message

字符 {expression} 不合法!{key}:value 中 value 里的 SQL函数必须为 function(arg0,arg1,...) 这种格式!

What it means

For an @column (or similar) expression containing '(', parseSQLExpression takes start as the first '(' and end as the last ')'; if start >= end the expression cannot be of the required function(arg0,arg1,...) shape and this IllegalArgumentException fires. It catches unbalanced or degenerate parenthesis placement before any SQL is emitted.

Source

Thrown at APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java:2555

					example = key + ":\"column0!=0;column1+3*2<=10;function0(arg0,arg1,...)>1;function1(...)%5<=3...\"";
				}
			}

			//有函数,但不是窗口函数
			int overIndex = expression.indexOf(")OVER(");  // 传参不传空格,拼接带空格  ") OVER (");
			int againstIndex = expression.indexOf(")AGAINST(");  // 传参不传空格,拼接带空格  ") AGAINST (");
			boolean containOver = overIndex > 0 && overIndex < expression.length() - ")OVER(".length();
			boolean containAgainst = againstIndex > 0 && againstIndex < expression.length() - ")AGAINST(".length();

			if (containOver && containAgainst) {
				throw new IllegalArgumentException("字符 " + expression + " 不合法!预编译模式下 " + example
						+ " 中 function 必须符合小写英文单词的 SQL 函数名格式!不能同时存在窗口函数关键词 OVER 和全文索引关键词 AGAINST!");
			}

			if (containOver == false && containAgainst == false) {
				int end = expression.lastIndexOf(')');
				if (start >= end) {
					throw new IllegalArgumentException("字符 " + expression + " 不合法!"
							+ key + ":value 中 value 里的 SQL函数必须为 function(arg0,arg1,...) 这种格式!");
				}
				String fun = expression.substring(0, start);
				if (fun.isEmpty() == false) {
					if (SQL_FUNCTION_MAP == null || SQL_FUNCTION_MAP.isEmpty()) {
						if (StringUtil.isName(fun) == false) {
							throw new IllegalArgumentException("字符 " + fun + " 不合法!预编译模式下 " + example
									+ " 中 function 必须符合小写英文单词的 SQL 函数名格式!");
						}
					} else if (SQL_FUNCTION_MAP.containsKey(fun) == false) {
						throw new IllegalArgumentException("字符 " + fun + " 不合法!预编译模式下 " + example
								+ " 中 function 必须符合小写英文单词的 SQL 函数名格式!且必须是后端允许调用的 SQL 函数!");
					}
				}

				String s = expression.substring(start + 1, end);
				boolean distinct = s.startsWith(PREFIX_DISTINCT);
				if (distinct) {

View on GitHub (pinned to 5284052872)

Solutions

  1. Balance the parentheses: exactly wrap args as fun(arg0,arg1,...).
  2. Add client-side validation that the expression's parens balance and the last char after the final ')' is ':' or end-of-string.
  3. Prefer server-side @raw for expressions the DSL struggles to express.
  4. Check for truncation at transport boundaries (URL encoding, field length limits).

Example fix

// before
{"User":{"@column":"concat(name,' - ',id"}}
// after
{"User":{"@column":"concat(name,' - ',id)"}}
Defensive patterns

Strategy: validation

Validate before calling

function balanced(s){let d=0;for(const c of s){if(c==='(')d++;if(c===')')d--;if(d<0)return false}return d===0}
const start=expr.indexOf('('),end=expr.lastIndexOf(')');
if(start<0||start>=end||!balanced(expr))throw new Error('expect fn(args) form');

Type guard

null

Try / catch

catch IllegalArgumentException; alert user to paren error and keep the input for editing

Prevention

When it happens

Trigger: "@column":"count(" , "@column":"count())" , "@column":"()" , or "@column":"concat(a,b))" where lastIndexOf(')') lands before/at the first '('. Also "@column":"now(" after client-side truncation.

Common situations: Client string truncation (fixed-length input fields) chopping off closing parens; hand-concatenating function calls and losing a paren; expressions like "cast(now() as date" where the closing paren was dropped in templating.

Related errors


AI-assisted analysis of Tencent/APIJSON@5284052872 (2026-08-14). Data as JSON: /api/errors/6a41f52ec03d4e1b. Report an issue: GitHub.