gin-gonic/gin · error

catch-all wildcard '${path}' in new path '${fullPath}' confl

Error message

catch-all wildcard '${path}' in new path '${fullPath}' conflicts with existing path segment '${pathSeg}' in existing prefix '${n.path}${pathSeg}'

What it means

Thrown by (*node).insertChild in tree.go:353 when a new catch-all route ('*') is being inserted under a node whose path already ends in '/' and which already has child segments. Because a catch-all would have to swallow everything after that slash, it cannot share the parent with pre-existing static or parametric children. The message names the conflicting child segment and the combined prefix so you can see what the catch-all would shadow.

Source

Thrown at tree.go:353

				continue
			}

			// Otherwise we're done. Insert the handle in the new leaf
			n.handlers = handlers
			return
		}

		// catchAll
		if i+len(wildcard) != len(path) {
			panic("catch-all routes are only allowed at the end of the path in path '" + fullPath + "'")
		}

		if len(n.path) > 0 && n.path[len(n.path)-1] == '/' {
			pathSeg := ""
			if len(n.children) != 0 {
				pathSeg, _, _ = strings.Cut(n.children[0].path, "/")
			}
			panic("catch-all wildcard '" + path +
				"' in new path '" + fullPath +
				"' conflicts with existing path segment '" + pathSeg +
				"' in existing prefix '" + n.path + pathSeg +
				"'")
		}

		// currently fixed width 1 for '/'
		i--
		if i < 0 || path[i] != '/' {
			panic("no / before catch-all in path '" + fullPath + "'")
		}

		n.path = path[:i]

		// First node: catchAll node with empty path
		child := &node{
			wildChild: true,
			nType:     catchAll,

View on GitHub (pinned to 34dac209ff)

Solutions

  1. Register the catch-all FIRST and the specific static children AFTER (Gin allows static children under a catch-all parent in many orderings, but the safe path is specific-before-general only when they do not share the immediate slash-parent — easiest is to test both orders).
  2. Move the catch-all to a distinct prefix that has no existing children, e.g. /assets/*filepath kept separate from /static/css.
  3. Use router.NoRoute(handler) or a middleware-based fallback instead of a catch-all route when you need a true 'match anything' behaviour without tree conflicts.
  4. If you genuinely need both /static/css and /static/*filepath, restructure so the catch-all parent is its own node: register /static/*filepath alone and handle /static/css inside the handler by branching on the param.

Example fix

// before
router.GET("/static/css", serveCSS)
router.GET("/static/*filepath", serveAll) // panics: conflicts with 'css'

// after — distinct prefixes, no overlap
router.GET("/static/css", serveCSS)
router.GET("/assets/*filepath", serveAll)

// or — single catch-all, branch inside the handler
router.GET("/static/*filepath", func(c *gin.Context) {
    if c.Param("filepath") == "/css" { serveCSS(c); return }
    serveAll(c)
})
Defensive patterns

Strategy: validation

Validate before calling

// Static catch-all conflict predictor: a '/*x' route conflicts if a sibling
// '/seg' is registered under the same parent. Maintain a registry.
type routeRegistry struct {
    parentChildren map[string]map[string]bool // "/static" -> {"css": true}
}
func (r *routeRegistry) canAddCatchAll(parent, catchallParent string) error {
    if kids, ok := r.parentChildren[catchallParent]; ok && len(kids)  0 {
        return fmt.Errorf("catch-all under %s conflicts with existing children %v", catchallParent, keys(kids))
    }
    return nil
}

Try / catch

func safeRegister(e *gin.Engine, method, path string, h gin.HandlerFunc) (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("catch-all conflict in %s: %v", path, r)
        }
    }()
    e.Handle(method, path, h)
    return nil
}

Prevention

When it happens

Trigger: Registering router.GET("/static/*filepath", h) AFTER router.GET("/static/css", h), or mounting a catch-all under a group that already mapped specific children such as /api/*all after /api/users and /api/orders. The catch-all is rejected because it would conflict with the existing segment 'css' (or 'users').

Common situations: Adding a catch-all / fallback route to an existing API tree; introducing router.NoRoute-style handling via a real route that overlaps already-registered children; ordering routes such that specific paths come before the wildcard.

Related errors


AI-assisted analysis of gin-gonic/gin@34dac209ff (2026-08-04). Data as JSON: /data/errors/62c52b76b8dbe274.json. Report an issue: GitHub.