{"id":"6db51eef48e4ccf2","repo":"gin-gonic/gin","slug":"pathseg-in-new-path-fullpath-conflicts-w","errorCode":null,"errorMessage":"'${pathSeg}' in new path '${fullPath}' conflicts with existing wildcard '${n.path}' in existing prefix '${prefix}'","messagePattern":"'(.+?)' in new path '(.+?)' conflicts with existing wildcard '(.+?)' in existing prefix '(.+?)'","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tree.go","lineNumber":230,"sourceCode":"\t\t\t\tn = n.children[len(n.children)-1]\n\t\t\t\tn.priority++\n\n\t\t\t\t// Check if the wildcard matches\n\t\t\t\tif len(path) >= len(n.path) && n.path == path[:len(n.path)] &&\n\t\t\t\t\t// Adding a child to a catchAll is not possible\n\t\t\t\t\tn.nType != catchAll &&\n\t\t\t\t\t// Check for longer wildcard, e.g. :name and :names\n\t\t\t\t\t(len(n.path) >= len(path) || path[len(n.path)] == '/') {\n\t\t\t\t\tcontinue walk\n\t\t\t\t}\n\n\t\t\t\t// Wildcard conflict\n\t\t\t\tpathSeg := path\n\t\t\t\tif n.nType != catchAll {\n\t\t\t\t\tpathSeg, _, _ = strings.Cut(pathSeg, \"/\")\n\t\t\t\t}\n\t\t\t\tprefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path\n\t\t\t\tpanic(\"'\" + pathSeg +\n\t\t\t\t\t\"' in new path '\" + fullPath +\n\t\t\t\t\t\"' conflicts with existing wildcard '\" + n.path +\n\t\t\t\t\t\"' in existing prefix '\" + prefix +\n\t\t\t\t\t\"'\")\n\t\t\t}\n\n\t\t\tn.insertChild(path, fullPath, handlers)\n\t\t\treturn\n\t\t}\n\n\t\t// Otherwise add handle to current node\n\t\tif n.handlers != nil {\n\t\t\tpanic(\"handlers are already registered for path '\" + fullPath + \"'\")\n\t\t}\n\t\tn.handlers = handlers\n\t\tn.fullPath = fullPath\n\t\treturn\n\t}","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/gin-gonic/gin/blob/34dac209ffb6ef85cc78c5d217bbb7ad001d68fd/tree.go#L212-L248","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nrouter.GET(\"/users/:id\", getUserByID)\nrouter.GET(\"/users/:name\", getUserByName) // panics: :name conflicts with :id\n\n// after — disambiguate with a static prefix\nrouter.GET(\"/users/:id\", getUserByID)\nrouter.GET(\"/users/by-name/:name\", getUserByName)","handlingStrategy":"validation","validationCode":"// Register every route inside a helper that builds a fresh Engine and\n// returns the first panic, so conflicts surface in tests not in prod.\nfunc buildRouter(routes []struct{ Method, Path string; H gin.HandlerFunc }) (*gin.Engine, error) {\n    var first error\n    defer func() {\n        if r := recover(); r != nil {\n            first = fmt.Errorf(\"route registration failed: %v\", r)\n        }\n    }()\n    e := gin.New()\n    for _, rt := range routes {\n        e.Handle(rt.Method, rt.Path, rt.H)\n    }\n    return e, first\n}","typeGuard":null,"tryCatchPattern":"// In Go, panic recovery at startup. Run route registration under recover.\nfunc safeRegister(e *gin.Engine, method, path string, h gin.HandlerFunc) (err error) {\n    defer func() {\n        if r := recover(); r != nil {\n            err = fmt.Errorf(\"wildcard conflict registering %s %s: %v\", method, path, r)\n        }\n    }()\n    e.Handle(method, path, h)\n    return nil\n}","preventionTips":["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."],"tags":["routing","wildcard-conflict","gin","radix-tree","startup-panic"],"analyzedSha":"34dac209ffb6ef85cc78c5d217bbb7ad001d68fd","analyzedAt":"2026-08-04T21:26:18.438Z","schemaVersion":2}