{"id":"0a2cfbab94683bd6","repo":"gin-gonic/gin","slug":"only-one-wildcard-per-path-segment-is-allowed-has","errorCode":null,"errorMessage":"only one wildcard per path segment is allowed, has: '${wildcard}' in path '${fullPath}'","messagePattern":"only one wildcard per path segment is allowed, has: '(.+?)' in path '(.+?)'","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tree.go","lineNumber":298,"sourceCode":"\t\t\t\tvalid = false\n\t\t\t}\n\t\t}\n\t\treturn path[start:], start, valid\n\t}\n\treturn \"\", -1, false\n}\n\nfunc (n *node) insertChild(path string, fullPath string, handlers HandlersChain) {\n\tfor {\n\t\t// Find prefix until first wildcard\n\t\twildcard, i, valid := findWildcard(path)\n\t\tif i < 0 { // No wildcard found\n\t\t\tbreak\n\t\t}\n\n\t\t// The wildcard name must only contain one ':' or '*' character\n\t\tif !valid {\n\t\t\tpanic(\"only one wildcard per path segment is allowed, has: '\" +\n\t\t\t\twildcard + \"' in path '\" + fullPath + \"'\")\n\t\t}\n\n\t\t// check if the wildcard has a name\n\t\tif len(wildcard) < 2 {\n\t\t\tpanic(\"wildcards must be named with a non-empty name in path '\" + fullPath + \"'\")\n\t\t}\n\n\t\tif wildcard[0] == ':' { // param\n\t\t\tif i > 0 {\n\t\t\t\t// Insert prefix before the current wildcard\n\t\t\t\tn.path = path[:i]\n\t\t\t\tpath = path[i:]\n\t\t\t}\n\n\t\t\tchild := &node{\n\t\t\t\tnType:    param,\n\t\t\t\tpath:     wildcard,","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/gin-gonic/gin/blob/34dac209ffb6ef85cc78c5d217bbb7ad001d68fd/tree.go#L280-L316","documentation":"Thrown by (*node).insertChild in tree.go:298 when findWildcard returns valid==false, meaning a single path segment contains more than one ':' or '*' wildcard marker (e.g. /:a:b or /:x*y). Gin permits at most one wildcard per segment because the boundary between two adjacent wildcards would be ambiguous to match at request time. The offending wildcard substring and full path are included in the message.","triggerScenarios":"Registering /orders/:customerID:productID (two params jammed together with no separator), /search/:q:scope, or /*a*b. Any segment where the byte after the first ':' or '*' contains another ':' or '*' triggers it.","commonSituations":"Trying to express two related parameters in one URL segment without a delimiter; typos like /:id: (trailing colon); converting a query-string style path (/search?q=a&scope=b) into a path-style one incorrectly.","solutions":["Separate the two parameters with a literal delimiter (usually '/'), e.g. /orders/:customerID/:productID.","If they must live in one segment, use a single param and parse it inside the handler: /orders/:compound where :compound = 'custID-prodID'.","Re-read the offending segment from the panic message and delete the extra ':' or '*'."],"exampleFix":"// before\nrouter.GET(\"/orders/:customerID:productID\", h) // panics\n\n// after — split into two segments\nrouter.GET(\"/orders/:customerID/:productID\", h)\n\n// or — single param, parse in handler\nrouter.GET(\"/orders/:compound\", func(c *gin.Context) {\n    parts := strings.SplitN(c.Param(\"compound\"), \"-\", 2)\n    // parts[0], parts[1]\n})","handlingStrategy":"validation","validationCode":"// Reject any segment containing more than one ':' or '*'.\nfunc validateSingleWildcardPerSegment(p string) error {\n    for _, seg := range strings.Split(p, \"/\") {\n        if strings.Count(seg, \":\")+\u0001strings.Count(seg, \"*\") \u0001 1 {\n            return fmt.Errorf(\"segment %q in path %q has more than one wildcard marker\", seg, p)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"func 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(\"invalid wildcard in %s: %v\", path, r)\n        }\n    }()\n    e.Handle(method, path, h)\n    return nil\n}","preventionTips":["Use '/' between parameters rather than mashing them together.","When two values must share a segment, encode them with a visible delimiter and parse in the handler.","Lint route strings so any segment matching /^[:*].*[:*]/ fails the build."],"tags":["routing","invalid-path","wildcard","gin","startup-panic"],"analyzedSha":"34dac209ffb6ef85cc78c5d217bbb7ad001d68fd","analyzedAt":"2026-08-04T21:26:18.438Z","schemaVersion":2}