mongodb/node-mongodb-native · error · MongoInvalidArgumentError

Option "readPreference" must be a ReadPreference instance

Error message

Option "readPreference" must be a ReadPreference instance

What it means

Thrown by getReadPreference when the readPreference option is neither a string nor an instance of ReadPreference. After attempting string->instance conversion, the function asserts instanceof ReadPreference and raises a MongoInvalidArgumentError if it fails. This is a client-side argument validation error, fully under the caller's control.

Solutions

  1. Pass a valid string mode: 'primary' | 'primaryPreferred' | 'secondary' | 'secondaryPreferred' | 'nearest'.
  2. Or construct a ReadPreference instance: new ReadPreference('secondary').
  3. If loading config from JSON, coerce to string before passing.
  4. Check for typos and stray wrapping objects in the option.

Example fix

// before
collection.find({}, { readPreference: { mode: 'secondary' } });

// after
collection.find({}, { readPreference: 'secondary' });
Defensive patterns

Strategy: type-guard

Validate before calling

const RP_MODES = ['primary','primaryPreferred','secondary','secondaryPreferred','nearest'] as const;
function asReadPreference(v: unknown) {
  if (typeof v === 'string' && (RP_MODES as readonly string[]).includes(v)) return v;
  if (v instanceof ReadPreference) return v;
  throw new TypeError(`Invalid readPreference: ${String(v)}`);
}

Type guard

import { ReadPreference } from 'mongodb';
function isValidReadPreference(v: unknown): v is string | ReadPreference {
  if (v instanceof ReadPreference) return true;
  if (typeof v === 'string') {
    return ['primary','primaryPreferred','secondary','secondaryPreferred','nearest'].includes(v);
  }
  return false;
}

Prevention

When it happens

Trigger: Fires at src/cmap/wire_protocol/shared.ts:23 when options.readPreference is a non-string, non-ReadPreference value (e.g. a plain object like {mode:'secondary'} or a number). Triggered on any read operation (find, aggregate, count, listDatabases) or when setting collection/db-level read preferences.

Common situations: Passing {mode: 'secondary'} instead of 'secondary' or new ReadPreference('secondary'); passing a string the driver cannot parse (e.g. 'SECONDARY' works but typos do not); migrating code that built read preference objects manually; spreading untyped config from env/JSON without coercion.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@dce7939f86 (2026-08-11). Data as JSON: /api/errors/8ea69d14e72e7000. Report an issue: GitHub.

Appendix: source

Thrown at src/cmap/wire_protocol/shared.ts:23

import type { ServerDescription } from '../../sdam/server_description';
import type { Topology } from '../../sdam/topology';
import { TopologyDescription } from '../../sdam/topology_description';
import type { Connection } from '../connection';

export interface ReadPreferenceOption {
  readPreference?: ReadPreferenceLike;
}

export function getReadPreference(options?: ReadPreferenceOption): ReadPreference {
  // Default to command version of the readPreference.
  let readPreference = options?.readPreference ?? ReadPreference.primary;

  if (typeof readPreference === 'string') {
    readPreference = ReadPreference.fromString(readPreference);
  }

  if (!(readPreference instanceof ReadPreference)) {
    throw new MongoInvalidArgumentError(
      'Option "readPreference" must be a ReadPreference instance'
    );
  }

  return readPreference;
}

export function isSharded(topologyOrServer?: Topology | Server | Connection): boolean {
  if (topologyOrServer == null) {
    return false;
  }

  if (topologyOrServer.description && topologyOrServer.description.type === ServerType.Mongos) {
    return true;
  }

  // NOTE: This is incredibly inefficient, and should be removed once command construction
  // happens based on `Server` not `Topology`.

View on GitHub (pinned to dce7939f86)