From f6b8e69065cdb29b0d27f8c1ebf49a1252954fbf Mon Sep 17 00:00:00 2001 From: Monaam Aouini Date: Tue, 29 Jul 2025 12:23:33 +0100 Subject: [PATCH] fix: Add helpful error messages for invalid wildcard routes in Express v5 --- lib/application.js | 58 ++++++++++++++++++++++++++++++++++++++++++-- test/app.all.js | 55 ++++++++++++++++++++++++++++++++++++++++++ test/app.use.js | 60 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 2 deletions(-) diff --git a/lib/application.js b/lib/application.js index cf6d78c741e..74c01a5b570 100644 --- a/lib/application.js +++ b/lib/application.js @@ -219,7 +219,34 @@ app.use = function use(fn) { fns.forEach(function (fn) { // non-express app if (!fn || !fn.handle || !fn.set) { - return router.use(path, fn); + try { + return router.use(path, fn); + } catch (err) { + // Check if this is a path-to-regexp error related to missing parameter names + if (err.message && err.message.includes('Missing parameter name')) { + // Check if the path contains a bare wildcard (more precise detection) + if (typeof path === 'string') { + // Detect common problematic wildcard patterns + var isBareWildcard = path === '*' || + path === '/*' || + /\/\*(?![a-zA-Z_$])/.test(path); // matches /* not followed by identifier chars + + if (isBareWildcard) { + var enhancedError = new TypeError( + 'Invalid middleware path: "' + path + '". ' + + 'In Express v5, wildcard paths must have named parameters. ' + + 'Use "/*splat" instead of "*" or "/*". ' + + 'See Express v5 migration guide for details.' + ); + enhancedError.code = 'INVALID_WILDCARD_PATH'; + enhancedError.originalError = err; + throw enhancedError; + } + } + } + // Re-throw the original error if it's not a wildcard issue + throw err; + } } debug('.use app under %s', path); @@ -254,7 +281,34 @@ app.use = function use(fn) { */ app.route = function route(path) { - return this.router.route(path); + try { + return this.router.route(path); + } catch (err) { + // Check if this is a path-to-regexp error related to missing parameter names + if (err.message && err.message.includes('Missing parameter name')) { + // Check if the path contains a bare wildcard (more precise detection) + if (typeof path === 'string') { + // Detect common problematic wildcard patterns + var isBareWildcard = path === '*' || + path === '/*' || + /\/\*(?![a-zA-Z_$])/.test(path); // matches /* not followed by identifier chars + + if (isBareWildcard) { + var enhancedError = new TypeError( + 'Invalid route path: "' + path + '". ' + + 'In Express v5, wildcard routes must have named parameters. ' + + 'Use "/*splat" instead of "*" or "/*". ' + + 'See Express v5 migration guide for details.' + ); + enhancedError.code = 'INVALID_WILDCARD_ROUTE'; + enhancedError.originalError = err; + throw enhancedError; + } + } + } + // Re-throw the original error if it's not a wildcard issue + throw err; + } }; /** diff --git a/test/app.all.js b/test/app.all.js index e4afca7d731..4a284e81487 100644 --- a/test/app.all.js +++ b/test/app.all.js @@ -35,4 +35,59 @@ describe('app.all()', function(){ .del('/tobi') .expect(404, done); }) + + describe('wildcard error handling', function(){ + it('should provide helpful error for bare wildcard "*"', function(){ + var app = express(); + + try { + app.all('*', function(req, res){ + res.end('wildcard'); + }); + throw new Error('Expected error was not thrown'); + } catch (err) { + if (err.code !== 'INVALID_WILDCARD_ROUTE') { + throw err; + } + // Verify the error message mentions Express v5 and provides solution + if (!err.message.includes('Express v5')) { + throw new Error('Error message should mention Express v5'); + } + if (!err.message.includes('/*splat')) { + throw new Error('Error message should suggest /*splat alternative'); + } + } + }) + + it('should provide helpful error for "/*" wildcard', function(){ + var app = express(); + + try { + app.all('/*', function(req, res){ + res.end('wildcard'); + }); + throw new Error('Expected error was not thrown'); + } catch (err) { + if (err.code !== 'INVALID_WILDCARD_ROUTE') { + throw err; + } + // Verify the error message is helpful + if (!err.message.includes('wildcard routes must have named parameters')) { + throw new Error('Error message should explain named parameter requirement'); + } + } + }) + + it('should still work with named wildcard "/*splat"', function(done){ + var app = express(); + + app.all('/*splat', function(req, res){ + res.end('splat: ' + req.params.splat); + }); + + request(app) + .get('/anything/here') + .expect(200, 'splat: anything,here', done); + }) + }) }) diff --git a/test/app.use.js b/test/app.use.js index 1d56aa3b029..9254559d579 100644 --- a/test/app.use.js +++ b/test/app.use.js @@ -540,3 +540,63 @@ describe('app', function(){ }) }) }) + +describe('wildcard error handling', function(){ + it('should provide helpful error for bare wildcard "*" in middleware', function(){ + var app = express(); + + try { + app.use('*', function(req, res, next){ + next(); + }); + throw new Error('Expected error was not thrown'); + } catch (err) { + if (err.code !== 'INVALID_WILDCARD_PATH') { + throw err; + } + // Verify the error message mentions Express v5 and provides solution + if (!err.message.includes('Express v5')) { + throw new Error('Error message should mention Express v5'); + } + if (!err.message.includes('/*splat')) { + throw new Error('Error message should suggest /*splat alternative'); + } + } + }) + + it('should provide helpful error for "/*" wildcard in middleware', function(){ + var app = express(); + + try { + app.use('/*', function(req, res, next){ + next(); + }); + throw new Error('Expected error was not thrown'); + } catch (err) { + if (err.code !== 'INVALID_WILDCARD_PATH') { + throw err; + } + // Verify the error message is helpful + if (!err.message.includes('wildcard paths must have named parameters')) { + throw new Error('Error message should explain named parameter requirement'); + } + } + }) + + it('should still work with named wildcard "/*splat" in middleware', function(done){ + var app = express(); + + app.use('/*splat', function(req, res, next){ + req.splatParam = req.params.splat; + next(); + }); + + app.get('/*splat', function(req, res){ + res.end('middleware splat: ' + req.splatParam); + }); + + request(app) + .get('/anything/here') + .expect(200, 'middleware splat: anything,here', done); + }) +})