sveltejs/kit · error · Error

routes.include and routes.exclude must be arrays

Error message

routes.include and routes.exclude must be arrays

What it means

The adapter's `routes` option lets you customize `_routes.json` include/exclude patterns to control which requests invoke the Worker. Both `routes.include` and `routes.exclude` must be arrays (Cloudflare's route-matching format). Passing a string, object, or other non-array value fails this validation at build time.

Source

Thrown at packages/adapter-cloudflare/utils.js:95

	return redirects;
}

/**
 * Generates the [_routes.json](https://developers.cloudflare.com/pages/functions/routing/#create-a-_routesjson-file)
 * file that dictates which routes invoke the Cloudflare Worker.
 * @param {import('@sveltejs/kit').Builder} builder
 * @param {string[]} client_assets
 * @param {string[]} redirects
 * @param {import('./index.js').AdapterOptions['routes']} routes
 * @returns {import('./index.js').RoutesJSONSpec}
 */
export function get_routes_json(builder, client_assets, redirects, routes) {
	const include = routes?.include ?? ['/*'];
	let exclude = routes?.exclude ?? ['<all>'];

	if (!Array.isArray(include) || !Array.isArray(exclude)) {
		throw new Error('routes.include and routes.exclude must be arrays');
	}

	if (include?.length === 0) {
		throw new Error('routes.include must contain at least one route');
	}

	if (include?.length > 100) {
		throw new Error('routes.include must contain 100 or fewer routes');
	}

	/** @type {Set<string>} */
	const transformed_rules = new Set();
	for (const rule of exclude) {
		if (rule === '<all>') {
			transformed_rules.add('<build>');
			transformed_rules.add('<files>');
			transformed_rules.add('<prerendered>');
			transformed_rules.add('<redirects>');

View on GitHub (pinned to 03f1687fe6)

Solutions

  1. Wrap each pattern in an array: routes: { include: ['/*'], exclude: ['/static/*'] }.
  2. Omit the property entirely to use the defaults (include: ['/*'], exclude: ['<all>']).
  3. Verify the adapter option shape against the adapter-cloudflare documentation.

Example fix

// before (svelte.config.js)
adapter: adapter({ routes: { include: '/api/*' } })

// after
adapter: adapter({ routes: { include: ['/api/*'] } })
Defensive patterns

Strategy: type-guard

Validate before calling

const routes = adapterOptions.routes;
if (routes && (!Array.isArray(routes.include ?? ['/*']) || !Array.isArray(routes.exclude ?? ['<all>']))) {
  throw new Error('routes.include and routes.exclude must be arrays');
}

Type guard

function isValidRoutesOption(routes) {
  return routes === undefined || (
    typeof routes === 'object' && routes !== null &&
    (routes.include === undefined || Array.isArray(routes.include)) &&
    (routes.exclude === undefined || Array.isArray(routes.exclude))
  );
}

Try / catch

try {
  await viteBuild();
} catch (e) {
  if (String(e).includes('must be arrays')) {
    console.error('Wrap routes.include / routes.exclude values in [ ... ]');
    process.exit(1);
  }
  throw e;
}

Prevention

When it happens

Trigger: Configuring adapter options with e.g. `routes: { include: '/api/*' }` (a string instead of array) or `routes: { exclude: 'assets/*' }` — validated inside get_routes_json during adapt.

Common situations: Copy-pasting a single route pattern from Cloudflare docs without array brackets; JSON5/JS config mistakes; typing a comma-separated string instead of an array of patterns.

Related errors


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