gofiber/fiber · error

multiple binding_source tags found on struct %s

Error message

multiple binding_source tags found on struct %s

What it means

Returned by getBindingPrecedence (bind.go:466) when reflect-scanning a struct finds more than one field carrying a `binding_source` struct tag. The binding_source tag customizes the order Bind().All() consults sources (uri/body/query/header/cookie); the design allows exactly one such tag per struct, with multiple sources comma-separated inside it. Two tags create an ambiguous precedence, so the whole bind is rejected and the error cached per type.

Source

Thrown at bind.go:466

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 9a4c7e57fe)

Solutions

  1. Use a single binding_source tag listing all sources in precedence order, e.g. `binding_source:"uri,query,body"`.
  2. Remove the duplicate tag and choose which field drives precedence.
  3. Run a startup sanity check that binds a zero value of each request struct to surface this before traffic.

Example fix

// before
type LoginReq struct {
    Email string `binding_source:"query"`
    Token string `binding_source:"header"`
}

// after — one tag, sources in precedence order
type LoginReq struct {
    Email string `binding_source:"uri,query"`
    Token string
}
Defensive patterns

Strategy: validation

Validate before calling

// At startup, bind a zero value of each request struct to surface tag errors early.
func validateBindStructs(types []any) error {
    for _, t := range types {
        if err := app.Bind().All(t); err != nil {
            if strings.Contains(err.Error(), "binding_source") {
                return fmt.Errorf("bad struct %T: %w", t, err)
            }
        }
    }
    return nil
}

Try / catch

if err := c.Bind().All(&req); err != nil {
    if strings.Contains(err.Error(), "multiple binding_source tags") {
        // struct has >1 binding_source tag — fix the struct definition
        return fiber.NewError(fiber.StatusBadRequest, "bad request schema")
    }
    return err
}

Prevention

When it happens

Trigger: Defining a struct like `type Req struct { A string `binding_source:"query"`; B string `binding_source:"header"` }` and passing it to Bind().All(). The reflect scan sees two tagged fields and errors before any binding happens.

Common situations: Refactoring a bind struct and adding a second binding_source tag instead of combining sources; copy-paste of tagged fields; misunderstanding that multiple sources go in one tag (comma-separated).

Related errors


AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04). Data as JSON: /data/errors/36e8ef1f7d27c4d1.json. Report an issue: GitHub.