gorilla/mux · info · SkipRouter
skip this router
Error message
skip this router
What it means
Sentinel mux.SkipRouter (mux.go:403) is a control-flow signal, not a failure. A WalkFunc returns SkipRouter to tell Router.walk (mux.go:413-415) to skip descending into the current route's subrouter. The library compares with == and continues; any other returned error aborts the walk.
Source
Thrown at mux.go:403
return r.NewRoute().Schemes(schemes...)
}
// BuildVarsFunc registers a new route with a custom function for modifying
// route variables before building a URL.
func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route {
return r.NewRoute().BuildVarsFunc(f)
}
// Walk walks the router and all its sub-routers, calling walkFn for each route
// in the tree. The routes are walked in the order they were added. Sub-routers
// are explored depth-first.
func (r *Router) Walk(walkFn WalkFunc) error {
return r.walk(walkFn, []*Route{})
}
// SkipRouter is used as a return value from WalkFuncs to indicate that the
// router that walk is about to descend down to should be skipped.
var SkipRouter = errors.New("skip this router")
// WalkFunc is the type of the function called for each route visited by Walk.
// At every invocation, it is given the current route, and the current router,
// and a list of ancestor routes that lead to the current route.
type WalkFunc func(route *Route, router *Router, ancestors []*Route) error
func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error {
for _, t := range r.routes {
err := walkFn(t, r, ancestors)
if err == SkipRouter {
continue
}
if err != nil {
return err
}
for _, sr := range t.matchers {
if h, ok := sr.(*Router); ok {
ancestors = append(ancestors, t)View on GitHub (pinned to db9d1d0073)
Solutions
- Return mux.SkipRouter directly (do not wrap it) from the WalkFunc to prune one subrouter.
- Remember the library compares with ==, so never errors.Wrap/errors.Join it.
- Return any non-SkipRouter error to abort the walk entirely.
Example fix
// before
r.Walk(func(rt *mux.Route, _ *mux.Router, _ []*mux.Route) error {
return errors.New("skip") // aborts the whole walk!
})
// after
r.Walk(func(rt *mux.Route, _ *mux.Router, _ []*mux.Route) error {
if isAdmin(rt) { return mux.SkipRouter }
return nil
}) Defensive patterns
Strategy: validation
Validate before calling
// In your WalkFunc, only return SkipRouter intentionally
func walk(rt *mux.Route, _ *mux.Router, _ []*mux.Route) error {
if shouldPrune(rt) { return mux.SkipRouter }
return nil
} Type guard
func isSkipRouter(err error) bool { return errors.Is(err, mux.SkipRouter) } Try / catch
err := r.Walk(fn)
if err != nil && !errors.Is(err, mux.SkipRouter) {
return err
} Prevention
- Never wrap SkipRouter; the library compares with ==.
- Return nil to continue, SkipRouter to prune, anything else to abort.
- Document which WalkFuncs prune subrouters.
When it happens
Trigger: Inside a WalkFunc you return mux.SkipRouter to prune a subrouter from the traversal, e.g. skipping an admin subrouter while generating docs for the public site.
Common situations: Generating route docs/diagnostics and pruning internal subrouters; OpenAPI generation skipping auth subrouters; accidentally wrapping SkipRouter so the library's == check no longer recognizes it.
Related errors
- method is not allowed
- no matching route was found
- key not found in metadata
- mux: duplicated route variable %q
- mux: number of parameters must be multiple of 2, got %v
AI-assisted analysis of gorilla/mux@db9d1d0073 (2026-08-04).
Data as JSON: /data/errors/0966225982f14d6e.json.
Report an issue: GitHub.