sindresorhus/got · error · TypeError

Parameters `search` and `searchParams` are mutually exclusiv

Error message

Parameters `search` and `searchParams` are mutually exclusive.

What it means

options-to-url.ts:39: `search` (a raw query string like `'a=1&b=2'`) and `searchParams` (a structured object/URLSearchParams) cannot both be set. They both target `url.search`, so providing both is ambiguous; Got refuses rather than silently concatenating or overwriting.

Source

Thrown at source/core/utils/options-to-url.ts:39

];

export default function optionsToUrl(origin: string, options: URLOptions): URL {
	if (options.path) {
		if (options.pathname) {
			throw new TypeError('Parameters `path` and `pathname` are mutually exclusive.');
		}

		if (options.search) {
			throw new TypeError('Parameters `path` and `search` are mutually exclusive.');
		}

		if (options.searchParams) {
			throw new TypeError('Parameters `path` and `searchParams` are mutually exclusive.');
		}
	}

	if (options.search && options.searchParams) {
		throw new TypeError('Parameters `search` and `searchParams` are mutually exclusive.');
	}

	if (!origin) {
		if (!options.protocol) {
			throw new TypeError('No URL protocol specified');
		}

		origin = `${options.protocol}//${options.hostname ?? options.host ?? ''}`;
	}

	const url = new URL(origin);

	if (options.path) {
		const searchIndex = options.path.indexOf('?');
		if (searchIndex === -1) {
			options.pathname = options.path;
		} else {
			options.pathname = options.path.slice(0, searchIndex);

View on GitHub (pinned to e3924aa1e5)

Solutions

  1. Standardize on one query API — `searchParams` (structured) is preferred for new code.
  2. If migrating, convert legacy `search` strings to `URLSearchParams`/objects once at the boundary and drop `search`.
  3. Audit shared defaults to ensure they don't carry `search` while per-call code uses `searchParams`.
  4. Add a type that marks the two fields mutually exclusive in your own option builder.

Example fix

// before
await got(url, {search: 'a=1', searchParams: {b: 2}});

// after
await got(url, {searchParams: {a: 1, b: 2}});
Defensive patterns

Strategy: validation

Validate before calling

function assertOneQueryField(options) {
  if (options.search && options.searchParams) {
    throw new TypeError('pass query via search or searchParams, not both');
  }
}

Type guard

const queryFieldsConsistent = (o: {search?: string; searchParams?: unknown}): boolean =>
  !(o.search && o.searchParams);

Prevention

When it happens

Trigger: Passing `{search: 'a=1', searchParams: {b: 2}}` in one options object, or merging defaults that carry `searchParams` with a call that sets `search` (or vice-versa).

Common situations: Refactoring from raw `search` strings to structured `searchParams` and leaving both fields populated; combining two example snippets; a helper that sets `search` while a caller adds `searchParams`.

Related errors


AI-assisted analysis of sindresorhus/got@e3924aa1e5 (2026-08-03). Data as JSON: /data/errors/20f4004949274a13.json. Report an issue: GitHub.