mongodb/node-mongodb-native · error · MongoInvalidArgumentError

Update operations require that all atomic operators have def

Error message

Update operations require that all atomic operators have defined values, but none were provided.

What it means

Thrown by hasAtomicOperators() when invoked with the ignoreUndefined option and every key of the update document has an undefined value. The driver uses this to reject updateOne/updateMany/bulk update statements where the operator payloads ($set, $inc, ...) collapse entirely to undefined, which would otherwise send a no-op or malformed command. It surfaces as a MongoInvalidArgumentError and protects against silently writing nothing. Call sites include update.ts, find_and_modify.ts, bulk/common.ts, and client_bulk_write/command_builder.ts.

Source

Thrown at src/utils.ts:481

        return true;
      }
    }
    return false;
  }

  const keys = Object.keys(doc);
  // In this case we need to throw if all the atomic operators are undefined.
  if (options?.ignoreUndefined) {
    let allUndefined = true;
    for (const key of keys) {
      // eslint-disable-next-line no-restricted-syntax
      if (doc[key] !== undefined) {
        allUndefined = false;
        break;
      }
    }
    if (allUndefined) {
      throw new MongoInvalidArgumentError(
        'Update operations require that all atomic operators have defined values, but none were provided.'
      );
    }
  }

  return keys.length > 0 && keys[0][0] === '$';
}

export function resolveTimeoutOptions<T extends Partial<TimeoutContextOptions>>(
  client: MongoClient,
  options: T
): T &
  Pick<
    MongoClient['s']['options'],
    'timeoutMS' | 'serverSelectionTimeoutMS' | 'waitQueueTimeoutMS' | 'socketTimeoutMS'
  > {
  const { socketTimeoutMS, serverSelectionTimeoutMS, waitQueueTimeoutMS, timeoutMS } =
    client.s.options;

View on GitHub (pinned to 3366c21a63)

Solutions

  1. Strip undefined values from the update payload before sending: filter the object so $set only contains defined values.
  2. If the update is genuinely empty, skip the updateOne call entirely rather than sending it.
  3. Default optional fields to concrete values or guard each field with a truthy check before adding it to $set.

Example fix

// before
await coll.updateOne({ _id }, { $set: { name: user.name, age: user.age } });
// user.name and user.age both undefined => MongoInvalidArgumentError

// after
const setFields = Object.fromEntries(
  Object.entries({ name: user.name, age: user.age }).filter(([, v]) => v !== undefined)
);
if (Object.keys(setFields).length === 0) return; // nothing to update
await coll.updateOne({ _id }, { $set: setFields });
Defensive patterns

Strategy: validation

Validate before calling

function cleanUpdate(update: Record<string, any>): Record<string, any> | null {
  const out: Record<string, any> = {};
  for (const [op, fields] of Object.entries(update)) {
    const defined = Object.fromEntries(Object.entries(fields).filter(([, v]) => v !== undefined));
    if (Object.keys(defined).length > 0) out[op] = defined;
  }
  return Object.keys(out).length > 0 ? out : null;
}

const clean = cleanUpdate({ $set: { name: user.name, age: user.age } });
if (clean) await coll.updateOne({ _id }, clean);

Type guard

function hasDefinedAtomicValues(doc: Record<string, any>): boolean {
  return Object.values(doc).some(v => v !== undefined);
}

Prevention

When it happens

Trigger: Calling `coll.updateOne({ _id }, { $set: { field: someValue } })` where someValue is undefined; building an update from optional TS object fields whose values are all undefined; spreading a possibly-empty partial into $set.

Common situations: TypeScript optional fields hydrated to undefined; dynamic update builders that conditionally assign keys but the condition is never true; payload deserialization dropping values; refactors that leave $set:{a:undefined}.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04). Data as JSON: /data/errors/ddbec88a31d46538.json. Report an issue: GitHub.