mongodb/node-mongodb-native · error · MongoInvalidArgumentError
Raw operations are not allowed
Error message
Raw operations are not allowed
What it means
Thrown as MongoInvalidArgumentError inside BulkOperationBase.raw() (src/bulk/common.ts:1089) when a replaceOne operation object contains a 'q' key. The 'q' field is part of the server-side wire-protocol update statement shape (q/u), not the user-facing API shape (filter/replacement); the driver forbids passing already-encoded 'raw' operations through the public bulkWrite path to keep the API contract clean.
Solutions
- Re-shape the operation to the user-facing form: { replaceOne: { filter: {...}, replacement: {...} } }.
- If you have wire-protocol ops from a log, map q -> filter and u -> replacement (for replace) before passing them in.
- Do not construct ops from generic { q, u } helpers; use the documented key names.
Example fix
// before
await coll.bulkWrite([
{ replaceOne: { q: { _id: 1 }, u: { name: 'Sam' } } }
]);
// after
await coll.bulkWrite([
{ replaceOne: { filter: { _id: 1 }, replacement: { name: 'Sam' } } }
]); Defensive patterns
Strategy: validation
Validate before calling
function isUserFacingReplaceOne(op: any): boolean {
return op && 'replaceOne' in op && !('q' in op.replaceOne) &&
'filter' in op.replaceOne && 'replacement' in op.replaceOne;
}
if (!isUserFacingReplaceOne(op)) {
throw new Error('replaceOne must use { filter, replacement }, not the wire { q, u } shape');
} Type guard
type UserReplaceOne = { replaceOne: { filter: Record<string, unknown>; replacement: Record<string, unknown> } };
function isUserFacingReplaceOne(op: unknown): op is UserReplaceOne {
if (op == null || typeof op !== 'object') return false;
const r = (op as any).replaceOne;
return r != null && typeof r === 'object' &&
!('q' in r) && 'filter' in r && 'replacement' in r;
} Try / catch
try {
await coll.bulkWrite(ops);
} catch (e) {
if (e instanceof MongoInvalidArgumentError && /Raw operations are not allowed/.test(e.message)) {
// map wire shape q/u -> filter/replacement, then retry
} else throw e;
} Prevention
- Never pass wire-protocol { q, u } payloads to bulkWrite; always use { filter, replacement }.
- When converting captured/logged ops, map q -> filter and u -> replacement.
- Avoid generic { q, u } factories; use named keys matching the public API.
- Add a lint/test that rejects ops containing a top-level 'q' key.
When it happens
Trigger: Passing { replaceOne: { q: {...}, u: {...} } } to bulkOp.raw(...) (or collection.bulkWrite). The check is `if ('q' in op.replaceOne)` at src/bulk/common.ts:1088.
Common situations: Re-using wire-protocol payloads captured from a logger/proxy, building ops from generic { q, u } factories, or porting code that previously manipulated the low-level op format. Rarely accidental; usually a structural mismatch between captured and user-facing op shapes.
Related errors
- Operation must be an object with an operation key
- Replacement document must not use atomic operators
- Argument "operations" must be an array of documents
- Argument "size" must be a non-negative number
- Bulk find operation must specify a selector
AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11).
Data as JSON: /api/errors/3a81aef9d1b278f1.
Report an issue: GitHub.
Appendix: source
Thrown at src/bulk/common.ts:1089
throw new MongoInvalidArgumentError('Operation must be an object with an operation key');
}
if ('insertOne' in op) {
const forceServerObjectId = this.shouldForceServerObjectId();
const document =
op.insertOne && op.insertOne.document == null
? // TODO(NODE-6003): remove support for omitting the `documents` subdocument in bulk inserts
(op.insertOne as Document)
: op.insertOne.document;
maybeAddIdToDocuments(this.collection, document, { forceServerObjectId });
return this.addToOperationsList(BatchType.INSERT, document);
}
if ('replaceOne' in op || 'updateOne' in op || 'updateMany' in op) {
if ('replaceOne' in op) {
if ('q' in op.replaceOne) {
throw new MongoInvalidArgumentError('Raw operations are not allowed');
}
const updateStatement = makeUpdateStatement(
op.replaceOne.filter,
op.replaceOne.replacement,
{ ...op.replaceOne, multi: false }
);
if (hasAtomicOperators(updateStatement.u)) {
throw new MongoInvalidArgumentError('Replacement document must not use atomic operators');
}
return this.addToOperationsList(BatchType.UPDATE, updateStatement);
}
if ('updateOne' in op) {
if ('q' in op.updateOne) {
throw new MongoInvalidArgumentError('Raw operations are not allowed');
}
const updateStatement = makeUpdateStatement(op.updateOne.filter, op.updateOne.update, {
...op.updateOne,View on GitHub (pinned to dce7939f86)