sveltejs/kit · error · Error

Cloudflare Pages' _routes.json should be configured from the

Error message

Cloudflare Pages' _routes.json should be configured from the adapter option of the SvelteKit plugin in your vite.config.js. See https://svelte.dev/docs/kit/adapter-cloudflare#Options-routes

What it means

adapter-cloudflare generates _routes.json itself from the adapter's `routes` option. A hand-written _routes.json in the project root or in the assets directory conflicts with that generation and can silently break routing, so adapt() throws and points you at the SvelteKit plugin configuration instead.

Source

Thrown at packages/adapter-cloudflare/index.js:33

} from './utils.js';
import { exactRegex } from '@rolldown/pluginutils';
import { getRequest } from '@sveltejs/kit/node';

const name = '@sveltejs/adapter-cloudflare';

/** @type {typeof import('./index.js').default} */
export default function (options = {}) {
	// Add a random query so we can reliably string-replace the stub
	const stub_import =
		import.meta.resolve('./src/virtual-cloudflare-workers.js') + '?' + crypto.randomUUID();
	return {
		name,
		async adapt(builder) {
			if (
				fs.existsSync('_routes.json') ||
				fs.existsSync(`${builder.config.files.assets}/_routes.json`)
			) {
				throw new Error(
					"Cloudflare Pages' _routes.json should be configured from the adapter option of the SvelteKit plugin in your vite.config.js. See https://svelte.dev/docs/kit/adapter-cloudflare#Options-routes"
				);
			}

			if (fs.existsSync(`${builder.config.files.assets}/_headers`)) {
				throw new Error(
					`The _headers file should be placed in the project root rather than the ${builder.config.files.assets} directory`
				);
			}

			if (fs.existsSync(`${builder.config.files.assets}/_redirects`)) {
				throw new Error(
					`The _redirects file should be placed in the project root rather than the ${builder.config.files.assets} directory`
				);
			}

			const { wrangler_config, building_for_cloudflare_pages } = validate_wrangler_config(
				options.config

View on GitHub (pinned to 03f1687fe6)

Solutions

  1. Delete the hand-written _routes.json from the project root and/or your static/assets directory.
  2. Configure routing via the adapter option instead, e.g. adapter({ routes: { include: ['/*'], exclude: ['/static/*'] } }).
  3. Re-run `vite build` so the adapter generates _routes.json from your option.

Example fix

// vite.config.js before
adapter({ routes: { include: ['/*'], exclude: ['<all>'] } });
// and delete: static/_routes.json
// after
adapter({ routes: { include: ['/*'], exclude: ['/img/*', '/fonts/*'] } });
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'node:fs';
if (fs.existsSync('_routes.json') || fs.existsSync('static/_routes.json')) {
  throw new Error('Remove hand-written _routes.json; configure the adapter routes option instead');
}

Try / catch

try {
  await viteBuild();
} catch (err) {
  if (/_routes.json should be configured from the adapter option/.test(err.message)) {
    console.error('Delete _routes.json and move routing rules into the adapter routes option');
  }
  throw err;
}

Prevention

When it happens

Trigger: A _routes.json file exists in the project root or inside the configured assets directory (default `static/`) when running `vite build` with the Cloudflare adapter.

Common situations: Migrating a plain Cloudflare Pages project to SvelteKit and keeping the old _routes.json; copying Cloudflare docs examples that place _routes.json in static/.

Related errors


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