{"record":{"id":"d1be20aa89587230","repo":"t8y2/dbx","slug":"bulk-update-requires-update-operators-such-as-set","errorCode":null,"errorMessage":"Bulk update requires update operators such as $set","messagePattern":"Bulk update requires update operators such as \\$set","errorType":"validation","errorClass":"java.lang.IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"agents/drivers/mongodb/src/main/java/com/dbx/agent/mongodb/MongoAgent.java","lineNumber":1682,"sourceCode":"    }\n\n    static boolean isUpdateOperatorDocument(Document doc) {\n        if (doc.isEmpty()) {\n            return false;\n        }\n        for (String key : doc.keySet()) {\n            if (!key.startsWith(\"$\")) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    static void requireBulkUpdateOperatorDocument(Document doc) {\n        if (!isUpdateOperatorDocument(doc)) {\n            // updateOne/updateMany are shell-style bulk updates here; replacements stay on the\n            // single-document save path so a broad filter cannot replace many documents by accident.\n            throw new IllegalArgumentException(\"Bulk update requires update operators such as $set\");\n        }\n    }\n\n    private static Object deleteDocument(JsonObject params) {\n        MongoClient c = requireClient();\n        String database = params.get(\"database\").getAsString();\n        String collection = params.get(\"collection\").getAsString();\n        String id = params.get(\"id\").getAsString();\n\n        var col = c.getDatabase(database).getCollection(collection);\n        var result = col.deleteOne(new Document(\"_id\", parseId(id)));\n        return Collections.singletonMap(\"deleted_count\", result.getDeletedCount());\n    }\n\n    private static Object deleteDocuments(JsonObject params) {\n        MongoClient c = requireClient();\n        String database = params.get(\"database\").getAsString();\n        String collection = params.get(\"collection\").getAsString();","sourceCodeStart":1664,"sourceCodeEnd":1700,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/mongodb/src/main/java/com/dbx/agent/mongodb/MongoAgent.java#L1664-L1700","documentation":"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.","triggerScenarios":"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.","commonSituations":"Migrating from update-with-replacement habits (SQL UPDATE-style field maps); refactors that drop the $set wrapper; copy-pasting a replaceOne document into updateMany.","solutions":["Wrap the fields in $set: `{\"$set\": {\"status\": \"archived\", \"note\": \"batch\"}}`.","Use the appropriate operator ($inc, $push, $addToSet, $unset) for the intended mutation.","If a true replacement is intended, replace only one document at a time via the save/replace path or a unique _id filter.","Add a pre-send check that at least one key starts with `$` when targeting updateOne/updateMany."],"exampleFix":"// before\n{\"updateMany\": {\"filter\": {\"stale\": true}, \"update\": {\"archived\": true}}}\n// after\n{\"updateMany\": {\"filter\": {\"stale\": true}, \"update\": {\"$set\": {\"archived\": true}}}}","handlingStrategy":"validation","validationCode":"// Java: bulk updates must contain at least one $ operator\nDocument update = /* parsed update document */;\nboolean hasOperator = update.keySet().stream().anyMatch(k -> k.startsWith(\"$\"));\nif (!hasOperator) {\n    update = new Document(\"$set\", update);\n}","typeGuard":"static boolean isUpdateOperatorDocument(Document doc) { return doc != null && doc.keySet().stream().anyMatch(k -> k.startsWith(\"$\")); }","tryCatchPattern":"try { agent.updateMany(filter, updateDoc); }\ncatch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"Bulk update requires update operators\")) {\n    // wrap fields in $set and retry, or route to single-doc replace path\n  } else throw e;\n}","preventionTips":["Always use $set/$unset/$inc in updateOne/updateMany payloads","Reserve whole-document replacement for the single-document save/replace path","Add a pre-send assertion that bulk update docs start with a $ operator"],"tags":["mongodb","validation","bulk-update","safety"],"backgroundTag":"missing-update-operator","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}