#17087·kit

`resolve()` does not percent-encode route parameters, so a redirect to a non-ASCII pathname fails

Author: StadlyCreated Sep 10, 2026Updated Sep 10, 2026

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

javascript
resolve('/[slug]', { slug: 'møte' }); // '/møte'

Used in a redirect:

javascript
// 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\370te

The 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 malformed from the decodeURI in packages/kit/src/exports/vite/dev/index.js
  • built — decode_pathname throws in respond.js, so the request is handed to handle() and never reaches the route

The same value works through an href, because the browser encodes it against the UTF-8 document:

svelte
<!-- 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:

javascript
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