labstack/echo · error
binding element must be a struct
Error message
binding element must be a struct
What it means
Returned by bindData when the destination passed to form binding is not a struct and not one of the supported map types. Echo's form/path/query/header binder uses reflection to iterate struct fields with binding tags; a non-struct destination (e.g. a bare *string, *int, or *[]string) has no fields to bind into, so the binder refuses it. This only fires for the 'form' source; param/query/header sources silently return nil for incompatible types assuming the data lives in the body.
Source
Thrown at bind.go:253
val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v[0]))
} else if isElemInterface {
// To maintain backward compatibility, we always bind to the first string value
// and not the slice of strings when dealing with map[string]any{}
val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v[0]))
} else {
val.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(v))
}
}
return nil
}
// !struct
if typ.Kind() != reflect.Struct {
if tag == "param" || tag == "query" || tag == "header" {
// incompatible type, data is probably to be found in the body
return nil
}
return errors.New("binding element must be a struct")
}
meta := bindMetaFor(typ)
for fi := range meta.fields { // iterate over all destination fields
fm := &meta.fields[fi]
structField := val.Field(fm.index)
if fm.anonymous {
if structField.Kind() == reflect.Pointer {
structField = structField.Elem()
}
}
if !structField.CanSet() {
continue
}
structFieldKind := structField.Kind()
inputFieldName := fm.tagName(tag)
if fm.anonymous && structFieldKind == reflect.Struct && inputFieldName != "" {
// if anonymous struct with query/param/form tags, report an errorView on GitHub (pinned to 05489dc173)
Solutions
- Wrap the primitive in a struct with a form tag, e.g. type Req struct{ Name string `form:"name"` }, and bind to &Req{}
- If you only need a single value use c.FormValue("name") or c.QueryParam("name") instead of Bind
- JSON/XML bodies do not go through bindData, so c.Bind with application/json supports any JSON-decodable type
Example fix
// before
var name string
c.Bind(&name)
// after
var req struct {
Name string `form:"name"`
}
c.Bind(&req) Defensive patterns
Strategy: validation
Validate before calling
// Before calling c.Bind, verify the destination is a struct pointer
func isStructPtr(v any) bool {
t := reflect.TypeOf(v)
return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
}
if !isStructPtr(target) {
return errors.New("bind target must be a pointer to struct")
} Type guard
func isStructPtr(v any) bool {
t := reflect.TypeOf(v)
return t != nil && t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
} Try / catch
if err := c.Bind(&req); err != nil {
if strings.Contains(err.Error(), "binding element must be a struct") {
return echo.NewHTTPError(http.StatusBadRequest, "invalid request structure")
}
return err
} Prevention
- Always define a request struct type with form/json tags for bindable data
- For single values use c.FormValue/c.QueryParam instead of c.Bind
- Add unit tests that call c.Bind with your struct to catch type issues at test time
When it happens
Trigger: Calling c.Bind(&someString) or BindBody where the target is *string/*int/*[]string and the Content-Type is application/x-www-form-urlencoded or multipart/form-data, so bindData is invoked with tag "form" and a non-struct destination.
Common situations: Developer tries to bind a single form field value directly to a primitive instead of wrapping it in a struct. Migrating a JSON handler (which uses the JSON serializer) to form handling and forgetting to change the destination type.
Related errors
- unknown type
- failed to parse form value, key: %s, err: %w
- failed to parse form values, key: %s, err: %w
- query/param/form tags are not allowed with anonymous struct
- binding to multipart.FileHeader struct is not supported, use
AI-assisted analysis of labstack/echo@05489dc173 (2026-08-04).
Data as JSON: /data/errors/eb29ca6c7f1c4c0d.json.
Report an issue: GitHub.