sveltejs/kit · error · Error

Param matcher must return a string, number, boolean, or bigi

Error message

Param matcher must return a string, number, boolean, or bigint

What it means

Param matchers convert the URL parameter string into a value for routing. After validation, `run_matcher` unwraps the validated result and requires the parsed value to be a string, number, boolean, or bigint, because these are the only types that can be meaningfully coerced into route params. Any other type (object, array, null, undefined) throws this error.

Source

Thrown at packages/kit/src/utils/routing.js:162

	const result = matcher['~standard'].validate(value);

	if (result instanceof Promise) {
		throw new Error('Async param matchers are not supported');
	}

	if (result.issues) {
		return { success: false };
	}

	const parsed = result.value;

	if (
		typeof parsed !== 'string' &&
		typeof parsed !== 'number' &&
		typeof parsed !== 'boolean' &&
		typeof parsed !== 'bigint'
	) {
		throw new Error('Param matcher must return a string, number, boolean, or bigint');
	}

	return { success: true, value: parsed };
}

/**
 * @param {RegExpMatchArray} match
 * @param {import('types').RouteParam[]} params
 * @param {Record<string, ParamMatcher>} matchers
 */
export function exec(match, params, matchers) {
	/** @type {Record<string, any>} */
	const result = {};

	const values = match.slice(1);
	const values_needing_match = values.filter((value) => value !== undefined);

	let buffered = 0;

View on GitHub (pinned to 03f1687fe6)

Solutions

  1. Change the matcher so the validated value is a primitive — parse objects in the route load instead
  2. Use `.transform(String)` or return the raw string when you need structured data downstream
  3. Add an explicit coercion step in the matcher so success values are string/number/boolean/bigint

Example fix

// before
const date = v.pipe(v.string(), v.transform((s) => new Date(s))); // returns object
// after
const date = v.pipe(v.string(), v.regex(/^\d{4}-\d{2}-\d{2}$/)); // keep as string, construct Date in +page.server.ts
Defensive patterns

Strategy: type-guard

Validate before calling

const r = matcher['~standard'].validate('probe');
if (!r.issues && !['string', 'number', 'boolean', 'bigint'].includes(typeof r.value)) {
	throw new Error('Matcher success value must be a primitive');
}

Type guard

function returnsPrimitive(matcher) {
	const r = matcher['~standard'].validate('probe');
	return r.issues ? false : ['string', 'number', 'boolean', 'bigint'].includes(typeof r.value);
}

Try / catch

try {
	const outcome = runMatcher(matcher, value);
} catch (e) {
	if (e.message.startsWith('Param matcher must return')) {
		// fix matcher to return string/number/boolean/bigint
	}
	throw e;
}

Prevention

When it happens

Trigger: A matcher's `~standard.validate` succeeds but returns a `value` that is an object, array, null, undefined, symbol, or function — e.g. a matcher that parses the param into a Date or a custom object and returns it.

Common situations: Custom matchers using Zod/Valibot schemas that `.transform()` the string into an object (like `z.coerce.date()`); returning undefined on success by mistake; a validator that passes through unknown input types unchanged.

Related errors


AI-assisted analysis of sveltejs/kit@03f1687fe6 (2026-09-02). Data as JSON: /api/errors/1aca054ce1bf0945. Report an issue: GitHub.