Tencent/APIJSON · error · IllegalArgumentException

${key}: { ${objKey}: value 中 value 类型错误,只能是 String 或 Map<Str

Error message

${key}: { ${objKey}: value 中 value 类型错误,只能是 String 或 Map<String, Object> {} !

What it means

Same method-directive parsing as error 100, but one level deeper: after the directive value is accepted as a Map, one of its inner table entries has a value that is neither a String (per-table tag) nor a Map<String,Object> (per-table attributes). AbstractParser throws IllegalArgumentException naming the offending objKey.

Source

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

		Set<Entry<String, Object>> set = obj == null ? new HashSet<>() : obj.entrySet();
		for (Entry<String, Object> objEntry : set) {
			String objKey = objEntry == null ? null : objEntry.getKey();
			if (objKey == null) {
				continue;
			}

			Map<String, Object> objAttrMap = new HashMap<>();
			objAttrMap.put(KEY_METHOD, keyMethod);
			keyObjectAttributesMap.put(objKey, objAttrMap);

			Object objVal = objEntry.getValue();
			Map<String, Object> objAttrJson = objVal instanceof Map<?, ?> ? JSON.getMap(obj, objKey) : null;
			if (objAttrJson == null) {
				if (objVal instanceof String) {
					objAttrMap.put(KEY_TAG, "".equals(objVal) ? objKey : objVal);
				}
				else {
					throw new IllegalArgumentException(key + ": { " + objKey + ": value 中 value 类型错误,只能是 String 或 Map<String, Object> {} !");
				}
			}
			else {
				boolean hasTag = false;
				for (Entry<String, Object> entry : objAttrJson.entrySet()) {
					String objAttrKey = entry == null ? null : entry.getKey();
					if (objAttrKey == null) {
						continue;
					}

					switch (objAttrKey) {
						case KEY_DATABASE:
						case KEY_DATASOURCE:
						case KEY_NAMESPACE:
						case KEY_CATALOG:
						case KEY_SCHEMA:
						case KEY_VERSION:
						case KEY_ROLE:

View on GitHub (pinned to 5284052872)

Solutions

  1. Make each inner value a tag string: "@put": {"User": "User"}
  2. Or an attribute object: "@put": {"User": {"tag": "User", "method": "PUT"}}
  3. Move id lists into the table object of the main request body, not into the directive map

Example fix

// before
{"@delete": {"Comment": [1, 2]}}
// after
{"@delete": {"Comment": "Comment"}, "Comment": {"id{}": [1, 2]}}
Defensive patterns

Strategy: validation

Validate before calling

for (const [tbl, v] of Object.entries(req['@put'] ?? {})) {
  if (!(typeof v === 'string' || (v && typeof v === 'object' && !Array.isArray(v)))) {
    throw new Error(`@put:{${tbl}} value must be a tag string or object`);
  }
}

Type guard

const isInnerDirectiveValue = v => typeof v === 'string' || (v != null && typeof v === 'object' && !Array.isArray(v));

Try / catch

try { await client.crud(req); } catch (e) { if (e.message.includes('只能是 String 或 Map')) reportBadDirectiveEntry(req, e.message); else throw e; }

Prevention

When it happens

Trigger: A request like "@put": {"User": 123} or "@delete": {"Comment": [1,2]} — the inner value under a table name inside the method directive map is a number/array/boolean instead of a tag string or attribute object.

Common situations: Developers pass a list of ids directly under a table inside the directive (thinking it is a batch payload); inconsistent JSON generation where inner values alternate between string tags and raw ids.

Related errors


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