How It Works Features Pricing Blog Error Guides
Log In Start Free Trial
Gin · Go

Fix RouteConflict: panic: wildcard segment ':id' conflicts with existing children in path '/users/:id' in Gin

This panic occurs at startup when Gin detects conflicting route definitions, such as a wildcard parameter segment and a static segment at the same path level. Fix it by restructuring your routes so that static paths are registered before or separately from parameterized paths, or by using route groups to avoid collisions.

Reading the Stack Trace

goroutine 1 [running]: runtime/debug.Stack() /usr/local/go/src/runtime/debug/stack.go:24 +0x5e panic({0x102840ea0, 0x14000116420}) /usr/local/go/src/runtime/panic.go:770 +0x124 github.com/gin-gonic/gin.(*node).addRoute(0x14000118300, {0x1028f1e60, 0xb}, {0x14000116420, 0x1}) /go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/tree.go:262 +0x9f0 github.com/gin-gonic/gin.(*Engine).addRoute(0x14000128680, {0x1028f0f20, 0x3}, {0x1028f1e60, 0xb}, {0x14000116420, 0x1, 0x1}) /go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/gin.go:364 +0x1f0 github.com/gin-gonic/gin.(*RouterGroup).handle(0x14000128698, {0x1028f0f20, 0x3}, {0x1028f1e60, 0xb}, {0x14000116420, 0x1, 0x1}) /go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/routergroup.go:98 +0x148 main.setupRouter(0x14000128680) /app/main.go:24 +0x2b8 main.main() /app/main.go:12 +0x48

Here's what each line means:

Common Causes

1. Static and parameterized segments at the same level

Registering /users/search and /users/:id causes a conflict because Gin's router cannot distinguish them.

r.GET("/users/:id", GetUser)
r.GET("/users/search", SearchUsers) // panic: conflicts with :id

2. Duplicate wildcard routes

Two routes use different parameter names at the same path position.

r.GET("/items/:itemID", GetItem)
r.GET("/items/:productID", GetProduct) // panic: different param name

3. Catch-all and parameterized conflict

A catch-all route with *filepath conflicts with a parameterized route at the same level.

r.GET("/files/:name", GetFile)
r.GET("/files/*filepath", ServeFiles) // panic: wildcard conflict

The Fix

Register static routes before parameterized routes within a route group. In Gin's radix tree, static segments take priority when registered first. Alternatively, use query parameters for search instead of a separate path segment.

Before (broken)
func setupRouter(r *gin.Engine) {
	r.GET("/users/:id", GetUser)
	r.GET("/users/search", SearchUsers)
}
After (fixed)
func setupRouter(r *gin.Engine) {
	users := r.Group("/users")
	{
		users.GET("/search", SearchUsers)  // static route first
		users.GET("/:id", GetUser)          // param route second
	}
}

// Alternative: use a single handler with query params
// r.GET("/users", ListOrSearchUsers)  // /users?q=alice
// r.GET("/users/:id", GetUser)

Testing the Fix

package main_test

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
)

func TestRoutes_NoConflict(t *testing.T) {
	gin.SetMode(gin.TestMode)
	r := gin.New()
	assert.NotPanics(t, func() {
		setupRouter(r)
	})
}

func TestSearchRoute(t *testing.T) {
	gin.SetMode(gin.TestMode)
	r := gin.New()
	setupRouter(r)

	req := httptest.NewRequest(http.MethodGet, "/users/search", nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)
}

func TestGetUserRoute(t *testing.T) {
	gin.SetMode(gin.TestMode)
	r := gin.New()
	setupRouter(r)

	req := httptest.NewRequest(http.MethodGet, "/users/123", nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	assert.Equal(t, http.StatusOK, w.Code)
}

Run your tests:

go test ./... -v

Pushing Through CI/CD

git checkout -b fix/gin-route-conflict,git add main.go,git commit -m "fix: reorder routes to resolve wildcard conflict",git push origin fix/gin-route-conflict

Your CI config should look something like this:

name: CI
on:
  pull_request:
    branches: [main]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: '1.22'
      - run: go mod download
      - run: go vet ./...
      - run: go test ./... -race -coverprofile=coverage.out
      - run: go build ./...

The Full Manual Process: 18 Steps

Here's every step you just went through to fix this one bug:

  1. Notice the error alert or see it in your monitoring tool
  2. Open the error dashboard and read the stack trace
  3. Identify the file and line number from the stack trace
  4. Open your IDE and navigate to the file
  5. Read the surrounding code to understand context
  6. Reproduce the error locally
  7. Identify the root cause
  8. Write the fix
  9. Run the test suite locally
  10. Fix any failing tests
  11. Write new tests covering the edge case
  12. Run the full test suite again
  13. Create a new git branch
  14. Commit and push your changes
  15. Open a pull request
  16. Wait for code review
  17. Merge and deploy to production
  18. Monitor production to confirm the error is resolved

Total time: 30-60 minutes. For one bug.

Or Let bugstack Fix It in Under 2 minutes

Every step above? bugstack does it automatically.

Step 1: Install the SDK

go get github.com/bugstack/sdk

Step 2: Initialize

import "github.com/bugstack/sdk"

func init() {
  bugstack.Init(os.Getenv("BUGSTACK_API_KEY"))
}

Step 3: There is no step 3.

bugstack handles everything from here:

  1. Captures the stack trace and request context
  2. Pulls the relevant source files from your GitHub repo
  3. Analyzes the error and understands the code context
  4. Generates a minimal, verified fix
  5. Runs your existing test suite
  6. Pushes through your CI/CD pipeline
  7. Deploys to production (or opens a PR for review)

Time from error to fix deployed: Under 2 minutes.

Human involvement: zero.

Try bugstack Free →

No credit card. 5-minute setup. Cancel anytime.

Deploying the Fix (Manual Path)

  1. Run go test ./... locally to confirm the routes register without panic.
  2. Open a pull request with the route restructuring.
  3. Wait for CI checks to pass on the PR.
  4. Have a teammate review and approve the PR.
  5. Merge to main and verify the deployment in staging before promoting to production.

Frequently Asked Questions

BugStack validates that all routes register without panics, runs your test suite, and confirms no existing endpoints are broken by the route restructuring.

BugStack never pushes directly to production. Every fix goes through a pull request with full CI checks, so your team can review it before merging.

Gin uses a radix tree for routing which is extremely fast but doesn't allow ambiguous path segments. This is a deliberate design tradeoff for performance.

Gin is tightly coupled to its httprouter-based tree. If you need more flexible routing, consider Echo or chi which handle overlapping routes differently.