gin-gonic/gin · error
'${pathSeg}' in new path '${fullPath}' conflicts with existi
Error message
'${pathSeg}' in new path '${fullPath}' conflicts with existing wildcard '${n.path}' in existing prefix '${prefix}' What it means
Thrown by (*node).addRoute in tree.go:230 when a new route's wildcard segment cannot coexist with an already-registered wildcard at the same tree position. Gin's radix tree (forked from httprouter) allows only one wildcard child per node, so two params with different names like /users/:id and /users/:name are ambiguous and rejected. The panic message prints the conflicting segment, the full new path, the existing wildcard, and the shared prefix so you can see both sides of the collision.
Source
Thrown at tree.go:230
n = n.children[len(n.children)-1]
n.priority++
// Check if the wildcard matches
if len(path) >= len(n.path) && n.path == path[:len(n.path)] &&
// Adding a child to a catchAll is not possible
n.nType != catchAll &&
// Check for longer wildcard, e.g. :name and :names
(len(n.path) >= len(path) || path[len(n.path)] == '/') {
continue walk
}
// Wildcard conflict
pathSeg := path
if n.nType != catchAll {
pathSeg, _, _ = strings.Cut(pathSeg, "/")
}
prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path
panic("'" + pathSeg +
"' in new path '" + fullPath +
"' conflicts with existing wildcard '" + n.path +
"' in existing prefix '" + prefix +
"'")
}
n.insertChild(path, fullPath, handlers)
return
}
// Otherwise add handle to current node
if n.handlers != nil {
panic("handlers are already registered for path '" + fullPath + "'")
}
n.handlers = handlers
n.fullPath = fullPath
return
}View on GitHub (pinned to 34dac209ff)
Solutions
- Pick ONE canonical param name for that segment and use it everywhere: change /users/:name to /users/:id so both routes share the same wildcard node.
- If the segments genuinely carry different semantics, move one to a distinct static prefix, e.g. /users/by-name/:name vs /users/:id.
- Audit every router.GET/POST/... call and every router.Group(...) that introduces a :param at the conflicting position; the panic message's 'existing prefix' field names the shared ancestor.
- Run your route-registration code in a unit test (router :=' gin.Default(); register all routes) so the panic surfaces at test time, not in production.
Example fix
// before
router.GET("/users/:id", getUserByID)
router.GET("/users/:name", getUserByName) // panics: :name conflicts with :id
// after — disambiguate with a static prefix
router.GET("/users/:id", getUserByID)
router.GET("/users/by-name/:name", getUserByName) Defensive patterns
Strategy: validation
Validate before calling
// Register every route inside a helper that builds a fresh Engine and
// returns the first panic, so conflicts surface in tests not in prod.
func buildRouter(routes []struct{ Method, Path string; H gin.HandlerFunc }) (*gin.Engine, error) {
var first error
defer func() {
if r := recover(); r != nil {
first = fmt.Errorf("route registration failed: %v", r)
}
}()
e := gin.New()
for _, rt := range routes {
e.Handle(rt.Method, rt.Path, rt.H)
}
return e, first
} Try / catch
// In Go, panic recovery at startup. Run route registration under recover.
func safeRegister(e *gin.Engine, method, path string, h gin.HandlerFunc) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("wildcard conflict registering %s %s: %v", method, path, r)
}
}()
e.Handle(method, path, h)
return nil
} Prevention
- Adopt a single canonical param name per semantic position across the whole codebase (e.g. always :id for primary keys).
- Centralise route registration in one package or one builder function so collisions are visually obvious.
- Write a startup test that builds the production router; any wildcard conflict panics in CI instead of at deploy.
- When mounting sub-routers via Group, double-check that the group prefix plus child paths do not redeclare an already-used :param name at the same depth.
When it happens
Trigger: Registering two routes whose wildcard names diverge at the same path position, e.g. router.GET("/users/:id", ...) followed by router.GET("/users/:name", ...). Also triggered by mixing a parametric segment with a static suffix that the tree cannot disambiguate, or by adding /api/:v1/resource after /api/:v2/resource on the same Engine.
Common situations: Refactoring route names during a rename, merging two routers (e.g. mounting a sub-router under /users that already declared :id), copy-pasting a route group and forgetting to rename the param consistently, or upgrading from an older Gin version where the conflict check was laxer.
Related errors
- catch-all wildcard '${path}' in new path '${fullPath}' confl
- handlers are already registered for path '${fullPath}'
- invalid escape string in path '${path}'
- only one wildcard per path segment is allowed, has: '${wildc
- wildcards must be named with a non-empty name in path '${ful
AI-assisted analysis of gin-gonic/gin@34dac209ff (2026-08-04).
Data as JSON: /data/errors/6db51eef48e4ccf2.json.
Report an issue: GitHub.