t8y2/dbx · error · java.lang.IllegalArgumentException

Bulk update requires update operators such as $set

Error message

Bulk update requires update operators such as $set

What it means

MongoAgent deliberately restricts bulk updateOne/updateMany to operator documents (updates containing keys like $set, $unset, $inc, $push). A plain replacement document would silently replace every matched document, so requireBulkUpdateOperatorDocument throws this IllegalArgumentException to prevent accidental mass replacement; replacements belong on the single-document save path.

Source

Thrown at agents/drivers/mongodb/src/main/java/com/dbx/agent/mongodb/MongoAgent.java:1682

    }

    static boolean isUpdateOperatorDocument(Document doc) {
        if (doc.isEmpty()) {
            return false;
        }
        for (String key : doc.keySet()) {
            if (!key.startsWith("$")) {
                return false;
            }
        }
        return true;
    }

    static void requireBulkUpdateOperatorDocument(Document doc) {
        if (!isUpdateOperatorDocument(doc)) {
            // updateOne/updateMany are shell-style bulk updates here; replacements stay on the
            // single-document save path so a broad filter cannot replace many documents by accident.
            throw new IllegalArgumentException("Bulk update requires update operators such as $set");
        }
    }

    private static Object deleteDocument(JsonObject params) {
        MongoClient c = requireClient();
        String database = params.get("database").getAsString();
        String collection = params.get("collection").getAsString();
        String id = params.get("id").getAsString();

        var col = c.getDatabase(database).getCollection(collection);
        var result = col.deleteOne(new Document("_id", parseId(id)));
        return Collections.singletonMap("deleted_count", result.getDeletedCount());
    }

    private static Object deleteDocuments(JsonObject params) {
        MongoClient c = requireClient();
        String database = params.get("database").getAsString();
        String collection = params.get("collection").getAsString();

View on GitHub (pinned to c0390bff16)

Solutions

  1. Wrap the fields in $set: `{"$set": {"status": "archived", "note": "batch"}}`.
  2. Use the appropriate operator ($inc, $push, $addToSet, $unset) for the intended mutation.
  3. If a true replacement is intended, replace only one document at a time via the save/replace path or a unique _id filter.
  4. Add a pre-send check that at least one key starts with `$` when targeting updateOne/updateMany.

Example fix

// before
{"updateMany": {"filter": {"stale": true}, "update": {"archived": true}}}
// after
{"updateMany": {"filter": {"stale": true}, "update": {"$set": {"archived": true}}}}
Defensive patterns

Strategy: validation

Validate before calling

// Java: bulk updates must contain at least one $ operator
Document update = /* parsed update document */;
boolean hasOperator = update.keySet().stream().anyMatch(k -> k.startsWith("$"));
if (!hasOperator) {
    update = new Document("$set", update);
}

Type guard

static boolean isUpdateOperatorDocument(Document doc) { return doc != null && doc.keySet().stream().anyMatch(k -> k.startsWith("$")); }

Try / catch

try { agent.updateMany(filter, updateDoc); }
catch (IllegalArgumentException e) {
  if (e.getMessage().contains("Bulk update requires update operators")) {
    // wrap fields in $set and retry, or route to single-doc replace path
  } else throw e;
}

Prevention

When it happens

Trigger: Calling updateMany with `"update": {"status": "archived", "note": "batch"}` (no $ operators) matching many documents; the same shape works on the single-doc save path, so users copy it to the bulk path.

Common situations: Migrating from update-with-replacement habits (SQL UPDATE-style field maps); refactors that drop the $set wrapper; copy-pasting a replaceOne document into updateMany.

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/d1be20aa89587230. Report an issue: GitHub.