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

  1. Return mux.SkipRouter directly (do not wrap it) from the WalkFunc to prune one subrouter.
  2. Remember the library compares with ==, so never errors.Wrap/errors.Join it.
  3. 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

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


AI-assisted analysis of gorilla/mux@db9d1d0073 (2026-08-04). Data as JSON: /data/errors/0966225982f14d6e.json. Report an issue: GitHub.