gin-gonic/gin · error
only one wildcard per path segment is allowed, has: '${wildc
Error message
only one wildcard per path segment is allowed, has: '${wildcard}' in path '${fullPath}' What it means
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.
Source
Thrown at tree.go:298
valid = false
}
}
return path[start:], start, valid
}
return "", -1, false
}
func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) {
for {
// Find prefix until first wildcard
wildcard, i, valid := findWildcard(path)
if i < 0 { // No wildcard found
break
}
// The wildcard name must only contain one ':' or '*' character
if !valid {
panic("only one wildcard per path segment is allowed, has: '" +
wildcard + "' in path '" + fullPath + "'")
}
// check if the wildcard has a name
if len(wildcard) < 2 {
panic("wildcards must be named with a non-empty name in path '" + fullPath + "'")
}
if wildcard[0] == ':' { // param
if i > 0 {
// Insert prefix before the current wildcard
n.path = path[:i]
path = path[i:]
}
child := &node{
nType: param,
path: wildcard,View on GitHub (pinned to 34dac209ff)
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 '*'.
Example fix
// before
router.GET("/orders/:customerID:productID", h) // panics
// after — split into two segments
router.GET("/orders/:customerID/:productID", h)
// or — single param, parse in handler
router.GET("/orders/:compound", func(c *gin.Context) {
parts := strings.SplitN(c.Param("compound"), "-", 2)
// parts[0], parts[1]
}) Defensive patterns
Strategy: validation
Validate before calling
// Reject any segment containing more than one ':' or '*'.
func validateSingleWildcardPerSegment(p string) error {
for _, seg := range strings.Split(p, "/") {
if strings.Count(seg, ":")+strings.Count(seg, "*") 1 {
return fmt.Errorf("segment %q in path %q has more than one wildcard marker", seg, p)
}
}
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("invalid wildcard in %s: %v", path, r)
}
}()
e.Handle(method, path, h)
return nil
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- wildcards must be named with a non-empty name in path '${ful
- invalid escape string in path '${path}'
- catch-all routes are only allowed at the end of the path in
- no / before catch-all in path '${fullPath}'
- '${pathSeg}' in new path '${fullPath}' conflicts with existi
AI-assisted analysis of gin-gonic/gin@34dac209ff (2026-08-04).
Data as JSON: /data/errors/0a2cfbab94683bd6.json.
Report an issue: GitHub.