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
- Find the two conflicting routes from the error message and rename or remove one
- Align trailingSlash export on both routes so they resolve to distinct output paths
- Ensure dynamic route params don't generate the same URL as an existing static route
- 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
- Standardize one trailingSlash setting app-wide
- Ensure dynamic params cannot equal existing static route paths
- Grep routes for duplicate path segments before large refactors
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
- Cannot save ${decoded} as ${parent} is already a file. See h
- Could not find prerendered page ${file} for route ${path}
- Cannot access cloudflare:workers in a prerenderable route
- Could not find entry point
- ${relative} does not exist
AI-assisted analysis of sveltejs/kit@03f1687fe6 (2026-09-02).
Data as JSON: /api/errors/37d99819d2899b05.
Report an issue: GitHub.