mongodb/node-mongodb-native · error · MongoInvalidArgumentError

URI option "${key}" cannot appear more than once in the conn

Error message

URI option "${key}" cannot appear more than once in the connection string

What it means

Thrown during option parsing when the same URI query parameter appears more than once, except for readPreferenceTags which is allowed to repeat. The driver does not know which value wins, so it rejects ambiguous duplicates as a MongoInvalidArgumentError. Values are collected via url.searchParams.getAll(key) and any key (other than readPreferenceTags) with length > 1 triggers the error.

Source

Thrown at src/connection_string.ts:292

  if (url.username !== '') {
    const auth: Document = {
      username: decodeURIComponent(url.username)
    };

    if (typeof url.password === 'string') {
      auth.password = decodeURIComponent(url.password);
    }

    urlOptions.set('auth', [auth]);
  }

  for (const key of url.searchParams.keys()) {
    const values = url.searchParams.getAll(key);

    const isReadPreferenceTags = /readPreferenceTags/i.test(key);

    if (!isReadPreferenceTags && values.length > 1) {
      throw new MongoInvalidArgumentError(
        `URI option "${key}" cannot appear more than once in the connection string`
      );
    }

    if (!isReadPreferenceTags && values.includes('')) {
      throw new MongoAPIError(`URI option "${key}" cannot be specified with no value`);
    }

    if (!urlOptions.has(key)) {
      urlOptions.set(key, values);
    }
  }

  const objectOptions = new CaseInsensitiveMap<unknown>(
    Object.entries(options).filter(([, v]) => v != null)
  );

  // Validate options that can only be provided by one of uri or object

View on GitHub (pinned to 3366c21a63)

Solutions

  1. Dedupe query parameters before constructing the URI: keep the last (or first) value per key.
  2. Build options via a Map/URLSearchParams and set each key once.
  3. Move the conflicting option into the MongoClient options object instead of the URI.
  4. Audit URI templates for accidental duplicate interpolation.

Example fix

// before
const uri = `${baseUri}&retryWrites=true`; // base already has retryWrites
// after
const params = new URLSearchParams(baseUri.split('?')[1]);
params.set('retryWrites', 'true');
const uri = `${baseUri.split('?')[0]}?${params}`;
Defensive patterns

Strategy: validation

Validate before calling

function dedupeUriParams(uri: string): string {
  const [base, query] = uri.split('?');
  if (!query) return uri;
  const params = new URLSearchParams();
  for (const [k, v] of new URLSearchParams(query)) {
    if (k === 'readPreferenceTags') params.append(k, v);
    else params.set(k, v);
  }
  return `${base}?${params}`;
}

Type guard

const hasUniqueParams = (uri: string): boolean => {
  const q = uri.split('?')[1];
  if (!q) return true;
  const seen = new Set<string>();
  for (const [k] of new URLSearchParams(q).entries()) {
    if (k !== 'readPreferenceTags' && seen.has(k)) return false;
    seen.add(k);
  }
  return true;
};

Try / catch

try {
  await client.connect();
} catch (e) {
  if (e instanceof MongoInvalidArgumentError && /cannot appear more than once/.test(e.message)) {
    return new MongoClient(dedupeUriParams(uri)).connect();
  }
  throw e;
}

Prevention

When it happens

Trigger: URI like mongodb://h/db?retryWrites=true&retryWrites=false; concatenating a base URI with an options string that re-adds a key; URL builders that append options without deduping; copy-paste that duplicated a line.

Common situations: Composing a URI from multiple config sources (e.g. base + per-env override) where both set retryWrites or appName; templating systems that blindly append; migrating options into the URI that were also left in the options object (the object-side dedup is separate, but URI-side dupes still error).

Related errors


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