sveltejs/kit · error

Cannot save ${decoded} as it is already a directory. See htt

Error message

Cannot save ${decoded} as it is already a directory. See https://svelte.dev/docs/kit/page-options#prerender-route-conflicts for more information

What it means

Two prerendered routes resolved to the same output path, where one route already wrote a directory at that path (e.g. a route `/foo` produced directory `foo/` containing `foo/index.html`) and now another route wants to save a file at exactly that path. The prerenderer aborts rather than clobber the directory.

Source

Thrown at packages/kit/src/core/postbuild/prerender.js:594

					if (!prerendered.redirects.has(decoded)) {
						prerendered.redirects.set(decoded, {
							status: response.status,
							location: resolved
						});

						prerendered.paths.push(decoded);
					}
				}
			} else {
				log.warn(`location header missing on redirect received from ${decoded}`);
			}

			return;
		}

		if (response.status === 200) {
			if (existsSync(dest) && statSync(dest).isDirectory()) {
				throw new Error(
					`Cannot save ${decoded} as it is already a directory. See https://svelte.dev/docs/kit/page-options#prerender-route-conflicts for more information`
				);
			}

			const dir = dirname(dest);

			if (existsSync(dir) && !statSync(dir).isDirectory()) {
				const parent = decoded.split('/').slice(0, -1).join('/');
				throw new Error(
					`Cannot save ${decoded} as ${parent} is already a file. See https://svelte.dev/docs/kit/page-options#prerender-route-conflicts for more information`
				);
			}

			mkdirSync(dir, { recursive: true });

			writeFileSync(dest, body);
			written.add(file);

View on GitHub (pinned to 03f1687fe6)

Solutions

  1. Find the two conflicting routes from the error message and rename or remove one
  2. Align trailingSlash export on both routes so they resolve to distinct output paths
  3. Ensure dynamic route params don't generate the same URL as an existing static route
  4. Consult https://svelte.dev/docs/kit/page-options#prerender-route-conflicts for resolution patterns

Example fix

// before — both /foo and /foo/ rendered (default trailingSlash)
// src/routes/foo/+page.js: export const trailingSlash = 'always';
// src/routes/foo/bar conflicting...

// after — pick one convention
// src/routes/foo/+page.js: export const trailingSlash = 'never'; // and remove the duplicate route
Defensive patterns

Strategy: validation

Validate before calling

// audit route dirs for overlapping normalized paths before build
const paths = ['src/routes/foo/+page.svelte', 'src/routes/foo/'];
const normalized = new Set();
for (const p of paths) {
  const key = p.replace(/^src\/routes/, '').replace(/\/+$/, '');
  if (normalized.has(key)) console.warn('Prerender conflict at', key);
  normalized.add(key);
}

Try / catch

try {
  await prerender();
} catch (e) {
  if (String(e.message).includes('already a directory')) {
    console.error('Two routes write to the same path; rename one route');
  }
  throw e;
}

Prevention

When it happens

Trigger: Having routes like `/foo` (page) and a second route whose normalized path is also `foo` after trailing-slash/trailing-encoding normalization (e.g. route `foo` with different trailing slash config) so save() computes the same `dest` for a file while a directory exists.

Common situations: Mixing trailingSlash settings between two overlapping routes; a dynamic route `[slug]` and a static route colliding after prerender normalization; duplicate endpoints that both return 200 for the same URL.

Related errors


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