Tencent/APIJSON · error · IllegalArgumentException

@join:value 中value不合法!必须为 &/Table0/key0,</Table1/key1,... 或

Error message

@join:value 中value不合法!必须为 &/Table0/key0,</Table1/key1,... 或 { '&/Table0/key0':{}, '</Table1/key1':{},... } 这种形式!

What it means

When @join is the map form, each entry's VALUE must itself be a Map (e.g. {"&/User/id":{}}). If any value is a string/number/array/etc., IllegalArgumentException is thrown describing both accepted notations. This fires before any path parsing, i.e. it is purely a shape check on the join map.

Source

Thrown at APIJSONORM/src/main/java/apijson/orm/AbstractParser.java:1610

		}


		Set<Entry<String, Object>> set = joinMap == null ? null : new LinkedHashSet<>(slashKeys);

		if (set == null || set.isEmpty()) {
			Log.e(TAG, "onJoinParse  set == null || set.isEmpty() >> return null;");
			return null;
		}

		List<Join<T, M, L>> joinList = new ArrayList<>();

		for (Entry<String, Object> e : set) {  // { &/User:{}, </Moment/id@":{}, @/Comment/toId@:{} }
			// 分割 /Table/key
			String path = e == null ? null : e.getKey();
			Object outer = path == null ? null : e.getValue();

			if (outer instanceof Map<?, ?> == false) {
				throw new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + ":value 中value不合法!"
						+ "必须为 &/Table0/key0,</Table1/key1,... 或 { '&/Table0/key0':{}, '</Table1/key1':{},... } 这种形式!");
			}

			int index = path == null ? -1 : path.indexOf("/");
			if (index < 0) {
				throw new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + ":value 中 value 值 " + path + " 不合法!"
						+ "必须为 &/Table0,</Table1/key1,@/Table1:alias2/key2,... 或 { '&/Table0':{}, '</Table1/key1':{},... } 这种形式!");
			}
			String joinType = path.substring(0, index); //& | ! < > ( ) <> () *
			//			if (StringUtil.isEmpty(joinType, true)) {
			//				joinType = "|"; // FULL JOIN
			//			}
			path = path.substring(index + 1);

			index = path.lastIndexOf("/");
			String tableKey = index < 0 ? path : path.substring(0, index); // User:owner
			int index2 = tableKey.lastIndexOf("/");
			String arrKey = index2 < 0 ? null : tableKey.substring(0, index2);

View on GitHub (pinned to 5284052872)

Solutions

  1. Use the plain string form if you need no per-entry options: "@join":"&/User/id"
  2. Keep map values as empty objects {}; put conditions in the joined table's object in the request
  3. Remove any scalar/array values attached to join entries
  4. Re-check the exact two accepted shapes in the error message

Example fix

// before
{"@join":{"&/User/id":"contactId"}}
// after
{"@join":"&/User/id","User":{"contactId@":"/User/id"}}
Defensive patterns

Strategy: validation

Validate before calling

if (join instanceof Map) {
  for (Object v : ((Map<?,?>) join).values()) {
    if (!(v instanceof Map)) throw new IllegalArgumentException("join entry value must be {}");
  }
}

Type guard

boolean joinMapValuesAreObjects(Map<?,?> joinMap) {
  return joinMap.values().stream().allMatch(v -> v instanceof Map);
}

Prevention

When it happens

Trigger: {"@join":{"&/User/id":"userId"}} — attaching a value to a join entry; or {"@join":{"&/User/id":[]}}. Users try to supply join conditions as scalar values on the entry.

Common situations: Assuming the map form carries ON-conditions as values (conditions belong inside the table object instead); converting string form to map form while keeping string values; misreading docs examples with placeholders.

Related errors


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