From 3fef36e8b65fda047dd2b5f63077b0878997cef0 Mon Sep 17 00:00:00 2001 From: Yash Singh Date: Thu, 18 Jun 2026 18:42:46 +0530 Subject: [PATCH] fix(ui): don't crash admin render on unparseable custom component route path A view path that path-to-regexp can't parse (e.g. a stray ':') threw an uncaught TypeError inside the route-matching find() callbacks, crashing the whole admin render. Catch the parse error and fall back to a literal path comparison so the bad pattern just doesn't match instead of taking down the panel. --- .../src/utilities/isPathMatchingRoute.spec.ts | 22 +++++++++++++++++++ .../ui/src/utilities/isPathMatchingRoute.ts | 19 +++++++++++----- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/packages/ui/src/utilities/isPathMatchingRoute.spec.ts b/packages/ui/src/utilities/isPathMatchingRoute.spec.ts index 369a5e39923..41090a66a63 100644 --- a/packages/ui/src/utilities/isPathMatchingRoute.spec.ts +++ b/packages/ui/src/utilities/isPathMatchingRoute.spec.ts @@ -181,4 +181,26 @@ describe('isPathMatchingRoute', () => { ).toBe(false) }) }) + + describe('paths that path-to-regexp cannot parse — regression for #15241', () => { + it('should not throw on a trailing bare colon', () => { + expect(() => + isPathMatchingRoute({ currentRoute: '/admin/foo', path: '/foo/:' }), + ).not.toThrow() + }) + + it('should not throw on consecutive colons', () => { + expect(() => + isPathMatchingRoute({ currentRoute: '/admin/foo', path: '/foo::bar' }), + ).not.toThrow() + }) + + it('should not match an unrelated route against an unparseable path', () => { + expect(isPathMatchingRoute({ currentRoute: '/dashboard', path: '/foo/:' })).toBe(false) + }) + + it('should fall back to literal equality for an unparseable path when exact', () => { + expect(isPathMatchingRoute({ currentRoute: '/foo/:', exact: true, path: '/foo/:' })).toBe(true) + }) + }) }) diff --git a/packages/ui/src/utilities/isPathMatchingRoute.ts b/packages/ui/src/utilities/isPathMatchingRoute.ts index 6c81b2e2c6e..297df9fff18 100644 --- a/packages/ui/src/utilities/isPathMatchingRoute.ts +++ b/packages/ui/src/utilities/isPathMatchingRoute.ts @@ -19,12 +19,21 @@ export const isPathMatchingRoute = ({ const keys = [] - const regex = pathToRegexp(viewPath, keys, { - sensitive, - strict, - }) + // A view path that is not a valid path-to-regexp pattern (e.g. a stray ":") + // makes pathToRegexp throw, which would crash the whole admin render since + // this runs inside route-matching `.find` callbacks. Treat an unparseable + // pattern as a literal string and fall back to plain comparison. + let match: null | RegExpExecArray = null + + try { + match = pathToRegexp(viewPath, keys, { + sensitive, + strict, + }).exec(currentRoute) + } catch { + match = null + } - const match = regex.exec(currentRoute) const viewRoute = match?.[0] || viewPath if (exact) {