`resolve()` does not percent-encode route parameters, so a redirect to a non-ASCII pathname fails
Describe the bug
resolve() substitutes parameter values verbatim, so a parameter holding non-ASCII characters yields a pathname that is not a valid URI.
That is harmless in an href, where the browser encodes it against the document, but not in a Location header, which is serialized as Latin-1.
Reproduction
resolve('/[slug]', { slug: 'møte' }); // '/møte'Used in a redirect:
// src/routes/+page.server.js
import { resolve } from '$app/paths';
import { redirect } from '@sveltejs/kit';
export function load() {
redirect(303, resolve('/[slug]', { slug: 'møte' }));
}ø (U+00F8) goes onto the wire as the single byte 0xF8:
location: /m\370teThe browser re-encodes that byte as %F8, which is not valid UTF-8, so the follow-up request to /m%F8te fails:
vite dev— 500,URI malformedfrom thedecodeURIinpackages/kit/src/exports/vite/dev/index.js- built —
decode_pathnamethrows inrespond.js, so the request is handed tohandle()and never reaches the route
The same value works through an href, because the browser encodes it against the UTF-8 document:
<!-- navigates to /m%C3%B8te, which routes fine -->
<a href={resolve('/[slug]', { slug: 'møte' })}>…</a>Expected
resolve() returns a pathname usable wherever a URL is expected — parameter values percent-encoded.
Rest parameters would need their / preserved, so this is encodeURIComponent per parameter rather than encodeURI over the result.
Workaround
Encode the values going in:
resolve('/[slug]', { slug: encodeURIComponent('møte') });encodeURI(resolve(...)) is not a substitute — encodeURI escapes %, so it double-encodes any query string already built with URLSearchParams.
System
Reproduced on SvelteKit 2.70.3.
Still applies on version-3: resolve_route returns a string parameter value unencoded, and encode_pathname_chars covers only %, /, ? and #.
Severity
annoyance
Source: sveltejs/kit