Tencent/APIJSON · error · IllegalArgumentException

HEAD请求: 字符{origin} 不合法!预编译模式下 @column:value 中 value里面用 , 分割的

Error message

HEAD请求: 字符{origin} 不合法!预编译模式下 @column:value 中 value里面用 , 分割的每一项 column:alias 中 column 必须是1个单词!如果有alias,则 alias 也必须为1个单词!并且不要有多余的空格!

What it means

Thrown in the HEAD/HEADS branch of gainColumnString when, in prepared mode, the column part (text before the last ':') of an @column item fails StringUtil.isName and also does not look like a function call — there is no '(' or the ')' does not come after it. The library only accepts a plain identifier or a function(arg,...) form inside HEAD @column, so anything else is rejected as a potential injection vector.

Source

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

							//column.remove(c);
							continue;
						}
					}

					int index = c.lastIndexOf(":"); //StringUtil.split返回数组中,子项不会有null
					String origin = index < 0 ? c : c.substring(0, index);
					String alias = index < 0 ? null : c.substring(index + 1);

					if (alias != null && StringUtil.isName(alias) == false) {
						throw new IllegalArgumentException("HEAD请求: 字符 " + alias
								+ " 不合法!预编译模式下 @column:value 中 value里面用 , 分割的每一项"
								+ " column:alias 中 column 必须是1个单词!如果有alias,则alias也必须为1个单词!并且不要有多余的空格!");
					}

					if (StringUtil.isName(origin) == false) {
						int start = origin.indexOf("(");
						if (start < 0 || origin.lastIndexOf(")") <= start) {
							throw new IllegalArgumentException("HEAD请求: 字符" + origin
									+ " 不合法!预编译模式下 @column:value 中 value里面用 , 分割的每一项"
									+ " column:alias 中 column 必须是1个单词!"
									+ "如果有alias,则 alias 也必须为1个单词!并且不要有多余的空格!");
						}

						if (start > 0 && StringUtil.isName(origin.substring(0, start)) == false) {
							throw new IllegalArgumentException("HEAD请求: 字符 " + origin.substring(0, start)
									+ " 不合法!预编译模式下 @column:value 中 value里面用 , 分割的每一项"
									+ " column:alias 中 column 必须是1个单词!如果有alias,则alias也必须为1个单词!并且不要有多余的空格!");
						}
					}
				}
			}

			boolean onlyOne = column != null && column.size() == 1;
			String c0 = onlyOne ? column.get(0) : null;

			if (onlyOne) {

View on GitHub (pinned to 5284052872)

Solutions

  1. Use a single column name: "@column":"id".
  2. If a function is used, use the exact form fun(arg) with balanced parentheses, e.g. "@column":"max(id)".
  3. Drop @column to get count(*).
  4. For qualified or complex expressions, configure them as server-side @raw entries so the prepared-mode parser skips them.

Example fix

// before
{"User":{"@column":"sum(id+1"}}
// after
{"User":{"@column":"sum(id)"}}
Defensive patterns

Strategy: validation

Validate before calling

const valid = /^[A-Za-z][A-Za-z0-9_]*(\([^()]*\))?$/.test(item);
if (!valid) throw new Error('HEAD @column must be a word or fn(args)');

Type guard

null

Try / catch

catch IllegalArgumentException, map to 400, echo the accepted syntax to the caller

Prevention

When it happens

Trigger: HEAD request with "@column":"count(id" (unbalanced parentheses), "@column":"id+1" (expression without parens), or "@column":"user.id" (dotted name is not a single word and has no function parens).

Common situations: Trying to count on a computed expression or a joined-table qualified column in a HEAD request; porting a GET query with complex @column to HEAD without simplifying; typo'd parentheses when hand-writing a function.

Related errors


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