-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Description
Describe the bug
Issue
I'm a bit newer to svelte kit, so it's a bit unclear if this is actually intended behavior, but I couldn't find any documentation around it, and the behavior feels like a bug.
Given a file structure like:
routes/
test/
+page.js // trailingSlash = 'always' configured here
+page.svelte
// important: no trailingSlash = 'always' is set at a higher level
If an http error (error()) is thrown in the loader for /test/, the client render/hydration will change the url.pathname to /test without the trailing slash. In our specific app, it actually caused an infinite redirect because of how we ensure trailing slash; however, for svelte kit, but you can still replicate in a brand new svelte kit app that it changes the url incorrectly, but doesn't seem to cause an infinite redirect.
This behavior only happens during client loading (via CSN to the page that errors) or client hydration after server render.
Example
Stack Blitz:
Cause
kit/packages/kit/src/runtime/client/client.js
Lines 1058 to 1068 in 3cf2b77
| const error_load = await load_nearest_error_page(i, branch, errors); | |
| if (error_load) { | |
| return get_navigation_result_from_branch({ | |
| url, | |
| params, | |
| branch: branch.slice(0, error_load.idx).concat(error_load.node), | |
| status, | |
| error, | |
| route | |
| }); | |
| } else { |
On this specific line:
branch: branch.slice(0, error_load.idx).concat(error_load.node),
Given a branch structure like:
layout
layout (error.svelte could be defined at this layer or above or not at all)
page (trailingSlash defined here)
The branch value passed into get_navigation_result_from_branch will remove all nodes below the error node which removes the trailing slash config and the url.pathname will get overridden to the default never setting:
kit/packages/kit/src/runtime/client/client.js
Lines 503 to 515 in 3cf2b77
| let slash = 'never'; | |
| // if `paths.base === '/a/b/c`, then the root route is always `/a/b/c/`, regardless of | |
| // the `trailingSlash` route option, so that relative paths to JS and CSS work | |
| if (base && (url.pathname === base || url.pathname === base + '/')) { | |
| slash = 'always'; | |
| } else { | |
| for (const node of branch) { | |
| if (node?.slash !== undefined) slash = node.slash; | |
| } | |
| } | |
| url.pathname = normalize_path(url.pathname, slash); |
Work around
To prevent this issue from happening, you need to just ensure that the closest error.svelte to the route is below where the trailingSlash = 'always' config is set.
So defining trailingSlash = 'always' in a root layout could fix the issue (if no error.svelte is configured in the app), or just actually defining an error.svelte in folder below where the trailingSlash is configured (and obviously above or at where the actual route loader is).
Reproduction
Logs
System Info
System:
OS: macOS 15.3.1
CPU: (12) arm64 Apple M3 Pro
Memory: 120.05 MB / 36.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 18.20.6 - ~/bin/node
npm: 10.8.2 - ~/bin/npm
Browsers:
Chrome: 133.0.6943.142
Safari: 18.3
npmPackages:
@sveltejs/adapter-auto: ^4.0.0 => 4.0.0
@sveltejs/kit: ^2.16.0 => 2.17.3
@sveltejs/vite-plugin-svelte: ^5.0.0 => 5.0.3
svelte: ^5.0.0 => 5.20.5
vite: ^6.0.0 => 6.2.0Severity
annoyance
Additional Information
No response

