Tencent/APIJSON · error · IllegalArgumentException

字符 不合法! :value 中 value 里的 SQL函数必须为 function(arg0,arg1,...)…

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.

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.

Appendix: 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)