Tencent/APIJSON · error · NullPointerException

PUT {}, {}/{} 不存在!

Error message

PUT {}, {}/{} 不存在!

What it means

Mirror of the conflict check for 'key-': when removing from a JSONArray-valued column, each element must already exist (targetArray.contains(obj)); otherwise NullPointerException '不存在!' is thrown with the key and index. Removal of a non-existent element is treated as a client error rather than a silent no-op.

Source

Thrown at APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java:766

				continue;
			}

			if (isAdd) {
				if (targetArray != null) {
					if (targetArray.contains(obj)) {
						throw new ConflictException("PUT " + path + ", " + key + "/" + i + " 已存在!");
					}
					targetArray.add(obj);
				} else {
					if (obj != null && obj instanceof Map == false) {
						throw new ConflictException("PUT " + path + ", " + key + "/" + i + " 必须为 JSONRequest {} !");
					}
					targetObj.putAll((Map) obj);
				}
			} else {
				if (targetArray != null) {
					if (targetArray.contains(obj) == false) {
						throw new NullPointerException("PUT " + path + ", " + key + "/" + i + " 不存在!");
					}
					targetArray.remove(obj);
				} else {
					if (obj instanceof String == false) {
						throw new ConflictException("PUT " + path + ", " + key + "/" + i + " 必须为 String 类型 !");
					}
					if (targetObj.containsKey(obj) == false) {
						throw new NullPointerException("PUT " + path + ", " + key + "/" + i + " 不存在!");
					}
					targetObj.remove(obj);
				}
			}
		}

		//add all 或 remove all >>>>>>>>>>>>>>>>>>>>>>>>>

		//PUT <<<<<<<<<<<<<<<<<<<<<<<<<
		sqlRequest.put(realKey, targetArray != null ? targetArray : JSON.toJSONString(targetObj)); // FIXME, SerializerFeature.WriteMapNullValue));

View on GitHub (pinned to 5284052872)

Solutions

  1. Client-side: only send elements confirmed present in the freshest fetched value.
  2. Catch NullPointerException on key- and re-fetch the row when stale state is suspected (optimistic-concurrency style).
  3. Serialize concurrent edits of the same JSON column to avoid racing removals.

Example fix

// before
remove("music"); // server tags = ["sport"] -> error
// after
List<String> del = wanted.stream().filter(currentTags::contains).collect(toList());
if (!del.isEmpty()) removeViaKeyMinus(del);
Defensive patterns

Strategy: try-catch

Validate before calling

JSONArray current = fetchArrayColumn(tableName, id, key);
JSONArray toRemove = new JSONArray();
for (Object o : payload) if (current.contains(o)) toRemove.add(o); // only existing items
if (!toRemove.isEmpty()) sendKeyMinus(key, toRemove);

Type guard

function filterPresent<T>(current: T[], remove: T[]): T[] {
  return remove.filter(x => current.some(c => JSON.stringify(c) === JSON.stringify(x)));
}

Try / catch

try { sendKeyMinus(...); } catch (NullPointerException e) { if (String.valueOf(e.getMessage()).contains("不存在")) { refetchRow(); // stale client state; reconcile and optionally retry } else throw e; }

Prevention

When it happens

Trigger: PUT { "tags-": ["music"] } where tags currently is ["sport"] — "music" is not in the array, so the removal aborts.

Common situations: Stale client state (UI shows a tag the server already removed); concurrent removals racing each other; retrying an already-applied removal.

Related errors


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