+page.svelte nested in three route groups around a parametric ancestorA leaf +page.svelte whose path nests inside three or more (group) segments — with a parametric ancestor — stops matching its bare URL on @sveltejs/[email protected]. The route still appears in the manifest and the param matcher is registered, but kit never selects it at runtime. event.route.id is null and the layout above the leaf does not run.
The observable response shape depends on whether a sibling route absorbs the URL:
Sibling under (meta)/ |
2.60.1 | 2.61.0 |
|---|---|---|
[[anything]]/+page.server.ts |
/123 → leaf 200 ✅ |
/123 → sibling 302 (the sibling's redirect) |
[anything]/+page.server.ts |
/123 → leaf 200 ✅ |
/123 → 404 |
| none | /123 → leaf 200 ✅ |
/123 → 404 |
In all three 2.61.0 cases the leaf (meta)/+page.svelte is the route that should match /123 — it just doesn't. The [[anything]] (optional) sibling is the only one that "absorbs" the orphaned URL (since [[ ]] matches with anything = undefined), turning a 404 into a silent 302 to wherever its load redirects. [anything] (required) cannot absorb the bare URL — it needs a segment — so the failure surfaces as a plain 404.
Same code on @sveltejs/[email protected] works correctly in every variant.
Suspected cause: PR #15861 ("avoid conflict for routes ending with optional parameters"), but the underlying problem is broader than that PR's scope — the leaf doesn't match even when no [[optional]] sibling exists.
pnpm install # installs kit 2.61.0 (broken)
pnpm dev
Then:
curl -sI http://localhost:5173/123 | head -1
# kit 2.61.0: HTTP/1.1 302 Found (and Location: /)
# kit 2.60.1: HTTP/1.1 200 OK
To check the 2.60.1 baseline against this same code:
pnpm add -D @sveltejs/[email protected]
rm -rf .svelte-kit
pnpm dev
Same route tree throughout. The only thing that varies between columns is whether a sibling exists under (meta)/ and whether the optional brackets are [[ ]] (optional) or [ ] (required).
| URL | 2.60.1 + [[anything]] |
2.60.1 + [anything] |
2.60.1 no sibling | 2.61.0 + [[anything]] |
2.61.0 + [anything] |
2.61.0 no sibling |
|---|---|---|---|---|---|---|
/123 |
200 ✅ | 200 ✅ | 200 ✅ | 302 → / ❌ |
404 ❌ | 404 ❌ |
/123/news |
200 ✅ | 200 ✅ | 200 ✅ | 200 ✅ | 200 ✅ | 200 ✅ |
/123/junk |
302 → /123 ✅ |
302 → /123 ✅ |
404 ✅ | 302 → /123 ✅ |
302 → /123 ✅ |
404 ✅ |
/ |
200 ✅ | 200 ✅ | 200 ✅ | 200 ✅ | 200 ✅ | 200 ✅ |
Key observations:
/123. The shape of the failure differs (302 vs 404) but the root cause is the same — (meta)/+page.svelte never matches /123.[[anything]] (optional) vs [anything] (required) only affects what happens when the leaf is missing. [[ ]] claims the bare URL because the optional rest is allowed to be empty; [ ] cannot, because it requires a segment./news) are unaffected — /123/news still matches news/+page.svelte in all six configurations. The breakage is isolated to the bare /{param} URL whose only possible match is the deeply nested leaf.(series) group (so the path becomes [id]/(view)/(meta)/+page.svelte) makes /123 match correctly on 2.61.0. Two groups is not enough.[id]) above the groups.(meta)/+page.svelte to (view)/+page.svelte (so only two groups remain around the leaf) restores matching on 2.61.0. Requires moving sibling routes too if they shared (meta).@sveltejs/[email protected] until an upstream patch lands.src/routes/
├── +layout.svelte
├── +page.svelte # /
└── (series)/ # <-- route group #1
└── [id]/ # parametric segment
└── (view)/ # <-- route group #2
├── +layout.svelte
├── (meta)/ # <-- route group #3
│ ├── +page.svelte # /{id} ← BROKEN on 2.61.0
│ └── [[anything]]/
│ └── +page.server.ts # /{id}/{anything} ← absorbs /{id} on 2.61.0
└── news/
└── +page.svelte # /{id}/news ← unaffected
@sveltejs/kit: 2.61.0 (broken) / 2.60.1 (working)svelte: 5.55.9vite: 7.3.3@sveltejs/vite-plugin-svelte: 6.2.4