gin-gonic/gin · error
catch-all routes are only allowed at the end of the path in
Error message
catch-all routes are only allowed at the end of the path in path '${fullPath}' What it means
Thrown by (*node).insertChild in tree.go:345 when a catch-all wildcard ('*name') is not positioned at the very end of the path. A catch-all by definition consumes the rest of the URL, so anything after it (e.g. /files/*filepath/extra) is unreachable. The check is i+len(wildcard) != len(path), meaning the wildcard must extend to the final byte.
Source
Thrown at tree.go:345
path = path[len(wildcard):]
child := &node{
priority: 1,
fullPath: fullPath,
}
n.addChild(child)
n = child
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 + "'")View on GitHub (pinned to 34dac209ff)
Solutions
- Move the catch-all to the end of the path: /download/:version/*file or /api/health/*proxy.
- If you need to capture the entire tail including a logical suffix, parse it inside the handler from c.Param("file") instead of expressing it in the route.
- Drop the trailing slash after the wildcard if it is the cause: /assets/*path rather than /assets/*path/.
Example fix
// before
router.GET("/download/*file/version", h) // panics
// after — catch-all last
router.GET("/download/:version/*file", h)
// or — parse the suffix in the handler
router.GET("/download/*file", func(c *gin.Context) {
parts := strings.SplitN(c.Param("file"), "/version", 2)
// parts[0] = file path, parts[1] (if present) = remainder
}) Defensive patterns
Strategy: validation
Validate before calling
// Reject catch-all wildcards that are not at the end of the path.
func validateCatchAllAtEnd(p string) error {
idx := strings.Index(p, "*")
if idx == -1 {
return nil
}
if idx != len(p)-1 && !strings.HasSuffix(p, "/") {
// find end of the wildcard segment
end := strings.IndexByte(p[idx:], '/')
if end != -1 {
return fmt.Errorf("catch-all in %q must be the last segment", 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("catch-all position invalid in %s: %v", path, r)
}
}()
e.Handle(method, path, h)
return nil
} Prevention
- Treat a '*' wildcard as the terminal segment of the route — nothing may follow it, not even a slash.
- Reorder path components so the catch-all is last.
- Move anything after the wildcard into the handler and parse it from c.Param.
When it happens
Trigger: Registering /download/*file/version, /api/*proxy/health, or /static/*css/style.css — any path where text follows the catch-all segment. Also triggered by trailing-slash confusion like /assets/*path/ (the slash after the wildcard counts as 'after').
Common situations: Designing a proxy or file-serving route and wanting to anchor something after the wildcard; refactoring a path and appending a suffix without realising the catch-all must come last.
Related errors
- no / before catch-all in 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
- catch-all wildcard '${path}' in new path '${fullPath}' confl
AI-assisted analysis of gin-gonic/gin@34dac209ff (2026-08-04).
Data as JSON: /data/errors/70fa15d95c0be189.json.
Report an issue: GitHub.