Skip to content

trailingSlash=always ignored on client render when error boundary is defined before/above node config #13516

Open
@ragboyjr

Description

@ragboyjr

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

Locally:
Image

Stack Blitz:

Image

Cause

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:

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

StackBlitz

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.0

Severity

annoyance

Additional Information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghelp wantedPRs welcomed. The implementation details are unlikely to cause debate

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions