From 0168d3384fc20e9ac2542619f65f7bb9edfe39f9 Mon Sep 17 00:00:00 2001 From: Vishal Rana Date: Mon, 5 Oct 2026 11:19:25 -0700 Subject: [PATCH 1/9] docs(group): note RouteNotFound must follow Use for catch-all paths Group.Use registers catch-all "" and "/*" RouteNotFound routes so group middleware runs for unmatched paths, which replaces custom handlers that were registered for those paths earlier (#3153). --- group.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/group.go b/group.go index 28ff93f84..37c3fff8e 100644 --- a/group.go +++ b/group.go @@ -183,6 +183,10 @@ func (g *Group) File(path, file string, middleware ...MiddlewareFunc) RouteInfo // RouteNotFound implements `Echo#RouteNotFound()` for sub-routes within the Group. // +// Important! When the group has middlewares, `Use` (re)registers catch-all `""` and `/*` NotFound routes for the +// group, replacing handlers registered for those paths before it. Register them after `Use` or pass the +// middlewares to `Group()`. +// // Example: `g.RouteNotFound("/*", func(c *echo.Context) error { return c.NoContent(http.StatusNotFound) })` func (g *Group) RouteNotFound(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo { return g.Add(RouteNotFound, path, h, m...) From 230d5b9410b70cd037b38b1691bb5523d095346b Mon Sep 17 00:00:00 2001 From: Vishal Rana Date: Mon, 5 Oct 2026 11:25:01 -0700 Subject: [PATCH 2/9] docs(group): note every Use call and the NoGroupAutoRegister404Routes opt-out --- group.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/group.go b/group.go index 37c3fff8e..a53cc1504 100644 --- a/group.go +++ b/group.go @@ -183,9 +183,9 @@ func (g *Group) File(path, file string, middleware ...MiddlewareFunc) RouteInfo // RouteNotFound implements `Echo#RouteNotFound()` for sub-routes within the Group. // -// Important! When the group has middlewares, `Use` (re)registers catch-all `""` and `/*` NotFound routes for the -// group, replacing handlers registered for those paths before it. Register them after `Use` or pass the -// middlewares to `Group()`. +// Important! When the group has middlewares, every `Use` call (re)registers catch-all `""` and `/*` NotFound routes +// for the group, replacing handlers registered for those paths before it (unless `NoGroupAutoRegister404Routes` is +// set). Register custom handlers for those paths after the last `Use` call. // // Example: `g.RouteNotFound("/*", func(c *echo.Context) error { return c.NoContent(http.StatusNotFound) })` func (g *Group) RouteNotFound(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo { From f463cbe2ad0bf8dc0afcad305af291f289485f90 Mon Sep 17 00:00:00 2001 From: Vishal Rana Date: Mon, 5 Oct 2026 11:31:24 -0700 Subject: [PATCH 3/9] docs(group): cover same-prefix groups and non-overwriting routers in RouteNotFound note --- group.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/group.go b/group.go index a53cc1504..89c2db042 100644 --- a/group.go +++ b/group.go @@ -184,8 +184,10 @@ func (g *Group) File(path, file string, middleware ...MiddlewareFunc) RouteInfo // RouteNotFound implements `Echo#RouteNotFound()` for sub-routes within the Group. // // Important! When the group has middlewares, every `Use` call (re)registers catch-all `""` and `/*` NotFound routes -// for the group, replacing handlers registered for those paths before it (unless `NoGroupAutoRegister404Routes` is -// set). Register custom handlers for those paths after the last `Use` call. +// for the group prefix (including calls made by `Echo.Group` and `Group.Group` for the same prefix), replacing +// handlers registered for those paths before it, unless `NoGroupAutoRegister404Routes` is set. Register custom +// handlers for those paths after the last such call. This needs a Router that allows overwriting routes (the +// `echo.New()` default); otherwise customise these 404s with `RouterConfig.NotFoundHandler`. // // Example: `g.RouteNotFound("/*", func(c *echo.Context) error { return c.NoContent(http.StatusNotFound) })` func (g *Group) RouteNotFound(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo { From 80a25b647fd6e55fadedbd44529f51cd127237d8 Mon Sep 17 00:00:00 2001 From: Vishal Rana Date: Mon, 5 Oct 2026 11:36:33 -0700 Subject: [PATCH 4/9] docs(group): document per-group option for non-overwriting routers in RouteNotFound note --- group.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/group.go b/group.go index 89c2db042..b1ad271a0 100644 --- a/group.go +++ b/group.go @@ -184,10 +184,11 @@ func (g *Group) File(path, file string, middleware ...MiddlewareFunc) RouteInfo // RouteNotFound implements `Echo#RouteNotFound()` for sub-routes within the Group. // // Important! When the group has middlewares, every `Use` call (re)registers catch-all `""` and `/*` NotFound routes -// for the group prefix (including calls made by `Echo.Group` and `Group.Group` for the same prefix), replacing -// handlers registered for those paths before it, unless `NoGroupAutoRegister404Routes` is set. Register custom -// handlers for those paths after the last such call. This needs a Router that allows overwriting routes (the -// `echo.New()` default); otherwise customise these 404s with `RouterConfig.NotFoundHandler`. +// for the group prefix (including calls made by `Echo#Group()` and `Group#Group()` for the same prefix), replacing +// handlers registered for those paths before it, unless `Config.NoGroupAutoRegister404Routes` is set. Register +// custom handlers for those paths after the last such call. This panics if the Router does not allow overwriting +// routes (`echo.New()` allows it); then set `Config.NoGroupAutoRegister404Routes` and register both paths yourself +// after `Use`, or use the router-wide `RouterConfig.NotFoundHandler`. // // Example: `g.RouteNotFound("/*", func(c *echo.Context) error { return c.NoContent(http.StatusNotFound) })` func (g *Group) RouteNotFound(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo { From eb4ba08c6fb34b0c7ed1ed4139380b7ef4eb5dbe Mon Sep 17 00:00:00 2001 From: Vishal Rana Date: Mon, 5 Oct 2026 11:46:28 -0700 Subject: [PATCH 5/9] docs(group): clarify RouteNotFound note scope and Echo-wide opt-out --- group.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/group.go b/group.go index b1ad271a0..ecf6fb201 100644 --- a/group.go +++ b/group.go @@ -185,10 +185,11 @@ func (g *Group) File(path, file string, middleware ...MiddlewareFunc) RouteInfo // // Important! When the group has middlewares, every `Use` call (re)registers catch-all `""` and `/*` NotFound routes // for the group prefix (including calls made by `Echo#Group()` and `Group#Group()` for the same prefix), replacing -// handlers registered for those paths before it, unless `Config.NoGroupAutoRegister404Routes` is set. Register -// custom handlers for those paths after the last such call. This panics if the Router does not allow overwriting -// routes (`echo.New()` allows it); then set `Config.NoGroupAutoRegister404Routes` and register both paths yourself -// after `Use`, or use the router-wide `RouterConfig.NotFoundHandler`. +// NotFound handlers registered for those paths before it, unless `Config.NoGroupAutoRegister404Routes` is set. +// Register custom ones for those paths after the last such call. Doing so panics if the Router does not allow +// overwriting routes (`echo.New()` allows it); then use the router-wide `RouterConfig.NotFoundHandler`, or set the +// Echo-wide `Config.NoGroupAutoRegister404Routes` (no group registers these routes) and register both paths +// yourself after `Use`. // // Example: `g.RouteNotFound("/*", func(c *echo.Context) error { return c.NoContent(http.StatusNotFound) })` func (g *Group) RouteNotFound(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo { From f4e83ff37bf32534667dd96ae4f045e05bb9cb99 Mon Sep 17 00:00:00 2001 From: Vishal Rana Date: Mon, 5 Oct 2026 11:52:33 -0700 Subject: [PATCH 6/9] docs(group): describe RouteNotFound replacement by full path --- group.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/group.go b/group.go index ecf6fb201..6723540e8 100644 --- a/group.go +++ b/group.go @@ -183,13 +183,14 @@ func (g *Group) File(path, file string, middleware ...MiddlewareFunc) RouteInfo // RouteNotFound implements `Echo#RouteNotFound()` for sub-routes within the Group. // -// Important! When the group has middlewares, every `Use` call (re)registers catch-all `""` and `/*` NotFound routes -// for the group prefix (including calls made by `Echo#Group()` and `Group#Group()` for the same prefix), replacing -// NotFound handlers registered for those paths before it, unless `Config.NoGroupAutoRegister404Routes` is set. -// Register custom ones for those paths after the last such call. Doing so panics if the Router does not allow -// overwriting routes (`echo.New()` allows it); then use the router-wide `RouterConfig.NotFoundHandler`, or set the -// Echo-wide `Config.NoGroupAutoRegister404Routes` (no group registers these routes) and register both paths -// yourself after `Use`. +// Important! Each `Use` call that leaves the group with middlewares, including the calls `Echo#Group()` and +// `Group#Group()` make when the new group has middlewares, (re)registers catch-all NotFound routes for the group +// prefix and prefix + `/*`. These replace NotFound handlers registered earlier for the same paths by any group or by +// Echo, unless `Config.NoGroupAutoRegister404Routes` is set. Register custom ones after the last such call for that +// prefix. Doing so panics if the Router does not allow overwriting routes (`echo.New()` allows it); then use the +// router-wide `RouterConfig.NotFoundHandler`, or set the Echo-wide `Config.NoGroupAutoRegister404Routes`, under +// which no group registers these routes and group middlewares run for unmatched paths only where you register both +// paths yourself after the last `Use`. // // Example: `g.RouteNotFound("/*", func(c *echo.Context) error { return c.NoContent(http.StatusNotFound) })` func (g *Group) RouteNotFound(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo { From c46c1ff6799b2823d71d853eb32ee4cc69dc5679 Mon Sep 17 00:00:00 2001 From: Vishal Rana Date: Mon, 5 Oct 2026 12:01:46 -0700 Subject: [PATCH 7/9] docs(group): state the RouteNotFound replacement rule instead of ordering recipes --- group.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/group.go b/group.go index 6723540e8..a978f173f 100644 --- a/group.go +++ b/group.go @@ -183,14 +183,13 @@ func (g *Group) File(path, file string, middleware ...MiddlewareFunc) RouteInfo // RouteNotFound implements `Echo#RouteNotFound()` for sub-routes within the Group. // -// Important! Each `Use` call that leaves the group with middlewares, including the calls `Echo#Group()` and -// `Group#Group()` make when the new group has middlewares, (re)registers catch-all NotFound routes for the group -// prefix and prefix + `/*`. These replace NotFound handlers registered earlier for the same paths by any group or by -// Echo, unless `Config.NoGroupAutoRegister404Routes` is set. Register custom ones after the last such call for that -// prefix. Doing so panics if the Router does not allow overwriting routes (`echo.New()` allows it); then use the -// router-wide `RouterConfig.NotFoundHandler`, or set the Echo-wide `Config.NoGroupAutoRegister404Routes`, under -// which no group registers these routes and group middlewares run for unmatched paths only where you register both -// paths yourself after the last `Use`. +// Important! When the group has middlewares, each `Use` call (re)registers catch-all NotFound routes for the group +// prefix and prefix + `/*` with the group middlewares, unless `Config.NoGroupAutoRegister404Routes` is set. For a +// given path the last registered NotFound route wins, and it runs only the group middlewares it was registered with: +// these catch-all routes replace handlers registered earlier for those paths (by any group or by Echo), and a +// handler registered later replaces them. Register custom ones on this group after its last `Use` to keep its +// middlewares. On a Router that does not allow overwriting routes this panics; use the router-wide +// `RouterConfig.NotFoundHandler` there, or set `Config.NoGroupAutoRegister404Routes` and register your own. // // Example: `g.RouteNotFound("/*", func(c *echo.Context) error { return c.NoContent(http.StatusNotFound) })` func (g *Group) RouteNotFound(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo { From 198702d3f03668919cafee155d403d80bdc0872b Mon Sep 17 00:00:00 2001 From: Vishal Rana Date: Mon, 5 Oct 2026 12:09:17 -0700 Subject: [PATCH 8/9] docs(group): name group creation as a catch-all trigger in RouteNotFound note --- group.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/group.go b/group.go index a978f173f..f6a8cc27e 100644 --- a/group.go +++ b/group.go @@ -183,13 +183,16 @@ func (g *Group) File(path, file string, middleware ...MiddlewareFunc) RouteInfo // RouteNotFound implements `Echo#RouteNotFound()` for sub-routes within the Group. // -// Important! When the group has middlewares, each `Use` call (re)registers catch-all NotFound routes for the group -// prefix and prefix + `/*` with the group middlewares, unless `Config.NoGroupAutoRegister404Routes` is set. For a -// given path the last registered NotFound route wins, and it runs only the group middlewares it was registered with: -// these catch-all routes replace handlers registered earlier for those paths (by any group or by Echo), and a -// handler registered later replaces them. Register custom ones on this group after its last `Use` to keep its -// middlewares. On a Router that does not allow overwriting routes this panics; use the router-wide -// `RouterConfig.NotFoundHandler` there, or set `Config.NoGroupAutoRegister404Routes` and register your own. +// Important! When the group has middlewares, each `Group#Use()` call (re)registers catch-all NotFound routes for +// the group prefix and prefix + `/*` with the group middlewares, unless the Echo-wide +// `Config.NoGroupAutoRegister404Routes` is set. `Echo#Group()` and `Group#Group()` make this call when the new group +// has own or inherited middlewares. For the same route path the last registered NotFound route wins, and it runs +// only the group middlewares it was registered with: these catch-all routes replace handlers registered earlier for +// those paths (by any group or by Echo), and a handler registered later replaces them. Register custom ones on this +// group after the last such call for its prefix to keep its middlewares. On a Router that does not allow overwriting +// routes this panics; use the router-wide `RouterConfig.NotFoundHandler` there, or set +// `Config.NoGroupAutoRegister404Routes` and register your own for every group whose middlewares should run for +// unmatched paths. // // Example: `g.RouteNotFound("/*", func(c *echo.Context) error { return c.NoContent(http.StatusNotFound) })` func (g *Group) RouteNotFound(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo { From 28ae43050d38653492cbc9ee18d26e00e6f2a4e4 Mon Sep 17 00:00:00 2001 From: Vishal Rana Date: Mon, 5 Oct 2026 12:14:10 -0700 Subject: [PATCH 9/9] docs(group): say both paths go on one group under NoGroupAutoRegister404Routes --- group.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/group.go b/group.go index f6a8cc27e..311eba047 100644 --- a/group.go +++ b/group.go @@ -191,8 +191,8 @@ func (g *Group) File(path, file string, middleware ...MiddlewareFunc) RouteInfo // those paths (by any group or by Echo), and a handler registered later replaces them. Register custom ones on this // group after the last such call for its prefix to keep its middlewares. On a Router that does not allow overwriting // routes this panics; use the router-wide `RouterConfig.NotFoundHandler` there, or set -// `Config.NoGroupAutoRegister404Routes` and register your own for every group whose middlewares should run for -// unmatched paths. +// `Config.NoGroupAutoRegister404Routes` and register both paths yourself on the group whose middlewares should run +// for them. // // Example: `g.RouteNotFound("/*", func(c *echo.Context) error { return c.NoContent(http.StatusNotFound) })` func (g *Group) RouteNotFound(path string, h HandlerFunc, m ...MiddlewareFunc) RouteInfo {