🐛 Fixed custom routes with :param paths being rejected on upload - #29523
Conversation
ref https://linear.app/ghost/issue/HKG-1940/ - the new route-settings validator rejected `:param` in every path, but route and collection keys are mounted on Express verbatim — nothing rewrites `{param}` to `:param` for them the way it does for permalinks and taxonomies - so `/author/:slug/` is a working wildcard route on live sites, and the error's suggested `/author/{slug}/` would mount a literal, unreachable path — the "fix" it recommended would have silently killed the route - legacy validate.js only ever checked `/:\w+` on collection permalinks and taxonomy values, so this restores parity there and keeps the tightening where legacy also tightened; permalinks and taxonomies still reject :param - also accepts `%s` as a data shorthand slug: fetch-data substitutes it with the request's slug param on channel routes and collections, which is what makes a wildcard route serve a different resource per request. There is no static slug that could replace it - 10 files in the Pro fleet depend on these two forms and now validate without needing edits; uploads accept them again, as they did before the migration
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
WalkthroughRoute settings validation now accepts Suggested reviewers: 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx run ghost:test:ci:integration |
✅ Succeeded | 2m 59s | View ↗ |
nx run ghost:test:integration |
✅ Succeeded | 3m 16s | View ↗ |
nx run ghost:test:legacy |
✅ Succeeded | 2m 57s | View ↗ |
nx run ghost:test:e2e |
✅ Succeeded | 2m 43s | View ↗ |
nx run ghost-monorepo:lint:boundaries |
✅ Succeeded | 20s | View ↗ |
nx run-many -t test:unit -p ghost |
✅ Succeeded | 33s | View ↗ |
nx run-many -t lint -p ghost,ghost-monorepo |
✅ Succeeded | 21s | View ↗ |
nx run-many --target=build --projects=tag:publi... |
✅ Succeeded | <1s | View ↗ |
nx run @tryghost/admin:build |
✅ Succeeded | 5s | View ↗ |
💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗
☁️ Nx Cloud last updated this comment at 2026-07-22 11:54:56 UTC

ref https://linear.app/ghost/issue/HKG-1940/
Problem
The new route-settings validator rejects
:paramnotation in every path. That's correct for collection permalinks and taxonomy values — but wrong for route and collection keys.Keys are mounted on Express verbatim. Nothing ever rewrites
{param}→:paramfor them, the wayactivation-bridgedoes for permalinks and taxonomies. So:/author/:slug//author/anything/, works/author/{slug}//author/%7Bslug%7D/— dead routeThe error even suggested the rewrite that breaks it:
Following that advice would silently kill a working route. On one affected site the
:paramroute mounts before taxonomies and shadows Ghost's built-in author router, so any edit there changes every author page.Legacy
validate.jsonly ever checked/:\w+on collection permalinks (:316) and taxonomy values (:390) — never on keys. These files have always been valid; only the new validator rejects them.Change
validatePathtakesallowParamNotation, set for route and collection keys only. Permalinks and taxonomies still reject:param, matching legacy.%sas the slug.fetch-datasubstitutes it with the request'sslugparam on channel routes and collections, which is what makes a wildcard route serve a different resource per request — there is no static slug that could replace it. Exactly one file in the fleet uses it, for per-language author/tag pages.Both are relaxations of a gate that is stricter than the validator it replaced, so no configuration that validates today stops validating.
Why not fix
{}to work instead?Considered and rejected. Making
{param}behave as a wildcard in keys would bring 45 currently-dead routes across 31 live sites to life at once — 22 of which pass validation today. One has/tag/{slug}/alongside atagtaxonomy (the route would start shadowing it); another has a collection at/{primary_tag}/that would become a root-level wildcard swallowing the site's entire URL space. Those owners wrote{slug}, got an inert route, and built around the result. Not something to change silently under them.Impact
routes.yamlfiles in the Pro fleet validate without any edit. Fleet failures: 51 → 41.Testing
throwstoaccepts; new coverage for%s, for%srejected when embedded mid-slug, for:paramstill rejected in a permalink even when the collection key uses it, and a serialize round-trip with a:paramkey (a YAML mapping key containing a colon, only expressible since this change).