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
- Wrap each pattern in an array: routes: { include: ['/*'], exclude: ['/static/*'] }.
- Omit the property entirely to use the defaults (include: ['/*'], exclude: ['<all>']).
- 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
- Always write route patterns as arrays of strings in svelte.config.js.
- Add a JS/TS config schema check (e.g. zod) around adapter options.
- Omit `routes` entirely unless you need custom patterns.
- Copy option examples from the official adapter-cloudflare docs, not Cloudflare route docs.
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
- The following _redirects rule cannot be excluded by _routes.
- routes.include must contain at least one route
- You must remove all `site` keys in ${config_path}. Consult h
- You must specify the `assets.directory` key in ${config_path
- You must specify the `assets.binding` key in ${config_path}
AI-assisted analysis of sveltejs/kit@03f1687fe6 (2026-09-02).
Data as JSON: /api/errors/d19807b8efc74eaf.
Report an issue: GitHub.