gofiber/fiber · error

multiple binding_source tags found on struct

Error message

multiple binding_source tags found on struct %s

What it means

The binder allows at most ONE field per struct to carry a `binding_source` tag (used to define custom binding precedence). More than one is ambiguous, so getBindingPrecedence caches and returns fmt.Errorf("multiple binding_source tags found on struct %s", t.Name()) at reflection time. The result is cached in bindingPrecedenceCache so subsequent calls fail fast.

Solutions

  1. Keep exactly one binding_source tag per struct.
  2. Combine multiple sources into a single comma-separated tag: `binding_source:"uri,body,query"`.
  3. Remove stale tags after refactors; add a unit test that binds a sample request to catch the error early.

Example fix

// before
type Req struct {
    ID   string `binding_source:"uri"`
    Body struct{} `binding_source:"body"`
}

// after
type Req struct {
    ID   string `binding_source:"uri"`
    Body struct{}
}
// or, if precedence must be explicit, put it on one field:
type Req struct {
    Combined CombinedInput `binding_source:"uri,body,query"`
}
Defensive patterns

Strategy: validation

Validate before calling

// At app startup, smoke-bind a sample request to each annotated struct
var req Req
if err := app.Test(...); err != nil { log.Fatalf("bind config: %v", err) }

Type guard

func hasOneBindingSource(t reflect.Type) int {
    n := 0
    for i := range t.NumField() {
        if t.Field(i).Tag.Get("binding_source") != "" { n++ }
    }
    return n
}

Prevention

When it happens

Trigger: Defining a struct with two (or more) fields annotated `binding_source:"..."` and binding a request to it.

Common situations: Copy-pasting binding annotations during refactors; merging structs without removing old tags; misunderstanding the one-tag-per-struct rule.

Related errors


AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11). Data as JSON: /api/errors/36e8ef1f7d27c4d1. Report an issue: GitHub.

Appendix: source

Thrown at bind.go:459

type cachedPrecedence struct {
	err     error
	sources []bindSource
}

var bindingPrecedenceCache sync.Map // map[reflect.Type]cachedPrecedence

func getBindingPrecedence(t reflect.Type) ([]bindSource, error) {
	if cached, ok := bindingPrecedenceCache.Load(t); ok {
		if cp, ok := cached.(cachedPrecedence); ok {
			return cp.sources, cp.err
		}
	}
	var precedence []bindSource
	var tagFound bool
	for i := range t.NumField() {
		if tag := t.Field(i).Tag.Get("binding_source"); tag != "" {
			if tagFound {
				err := fmt.Errorf("multiple binding_source tags found on struct %s", t.Name())
				bindingPrecedenceCache.Store(t, cachedPrecedence{err: err, sources: nil})
				return nil, err
			}
			tagFound = true

			parts := strings.SplitSeq(tag, ",")
			for p := range parts {
				sourceName := strings.TrimSpace(p)
				if sourceName == "" {
					continue
				}
				var source bindSource
				switch sourceName {
				case "uri":
					source = sourceURI
				case "body":
					source = sourceBody
				case "query":

View on GitHub (pinned to a105acad6c)