gin-gonic/gin · error
Bind struct can not be a pointer. Example: Use: gin.Bind(St
Error message
Bind struct can not be a pointer. Example:
Use: gin.Bind(Struct{}) instead of gin.Bind(&Struct{})
What it means
Thrown by gin.Bind in utils.go:32 when the value passed to Bind is a reflect.Pointer. gin.Bind is a middleware factory: it records the struct TYPE so that, on each request, it can reflect.New(typ) a fresh zero-value to deserialize into. Passing &Struct{} would cause every request to share the same instance, so the function refuses a pointer and asks for the value form. The message shows the intended usage: gin.Bind(Struct{}) not gin.Bind(&Struct{}).
Source
Thrown at utils.go:32
"runtime"
"strings"
"unicode"
)
// BindKey indicates a default bind key.
const BindKey = "_gin-gonic/gin/bindkey"
// localhostIP indicates the default localhost IP address.
const localhostIP = "127.0.0.1"
// localhostIPv6 indicates the default localhost IPv6 address.
const localhostIPv6 = "::1"
// Bind is a helper function for given interface object and returns a Gin middleware.
func Bind(val any) HandlerFunc {
value := reflect.ValueOf(val)
if value.Kind() == reflect.Ptr {
panic(`Bind struct can not be a pointer. Example:
Use: gin.Bind(Struct{}) instead of gin.Bind(&Struct{})
`)
}
typ := value.Type()
return func(c *Context) {
obj := reflect.New(typ).Interface()
if c.Bind(obj) == nil {
c.Set(BindKey, obj)
}
}
}
// WrapF is a helper function for wrapping http.HandlerFunc and returns a Gin middleware.
func WrapF(f http.HandlerFunc) HandlerFunc {
return func(c *Context) {
f(c.Writer, c.Request)
}View on GitHub (pinned to 34dac209ff)
Solutions
- Pass the struct by value: gin.Bind(LoginRequest{}). Bind records the type and allocates a fresh pointer per request internally.
- If you need binding but not as middleware, use c.ShouldBind(&obj) inside the handler instead of gin.Bind.
- Double-check you are calling gin.Bind (package-level middleware factory) and not c.ShouldBind / c.Bind (per-request methods on Context) — the conventions differ.
Example fix
// before
router.Use(gin.Bind(&LoginRequest{})) // panics: pointer not allowed
// after — pass by value, Bind allocates per request
router.Use(gin.Bind(LoginRequest{}))
// alternative — bind inside the handler with a pointer (correct here)
router.POST("/login", func(c *gin.Context) {
var req LoginRequest
if err := c.ShouldBind(&req); err != nil { c.Status(400); return }
// ...
}) Defensive patterns
Strategy: type-guard
Validate before calling
// Verify the value passed to gin.Bind is not a pointer before calling it.
func safeBind(v any) (gin.HandlerFunc, error) {
if reflect.ValueOf(v).Kind() == reflect.Ptr {
return nil, fmt.Errorf("gin.Bind requires a value, got pointer %T", v)
}
return gin.Bind(v), nil
} Type guard
// isBindValue reports whether v is an acceptable argument for gin.Bind.
func isBindValue(v any) bool {
return reflect.ValueOf(v).Kind() != reflect.Ptr
} Try / catch
func mustBind(v any) (mw gin.HandlerFunc) {
defer func() {
if r := recover(); r != nil {
panic(fmt.Errorf("gin.Bind(%T): %v\nUse gin.Bind(Struct{}) not gin.Bind(&Struct{})", v, r))
}
}()
return gin.Bind(v)
} Prevention
- Memorise the rule: gin.Bind takes a VALUE (Struct{}), c.ShouldBind takes a POINTER (&obj).
- Wrap every gin.Bind call site with a small helper that type-checks first.
- Code-review any router.Use(gin.Bind(...)) for a stray '&' in front of the struct literal.
- If the per-request pointer semantics are what you want, use c.ShouldBind(&obj) inside the handler instead of the middleware.
When it happens
Trigger: Calling router.Use(gin.Bind(&LoginRequest{})) or gin.Bind(&User{}) at router setup time. Any time the argument to gin.Bind is an address-of expression rather than a struct literal.
Common situations: Migrating from c.ShouldBind(&obj) (where a pointer IS correct at call site) to the Bind middleware factory and passing the pointer out of habit; copy-pasting a struct literal that already had '&' in front; reading examples for the wrong Bind API.
Related errors
- unknown type
- '${pathSeg}' in new path '${fullPath}' conflicts with existi
- handlers are already registered for path '${fullPath}'
- invalid escape string in path '${path}'
- only one wildcard per path segment is allowed, has: '${wildc
AI-assisted analysis of gin-gonic/gin@34dac209ff (2026-08-04).
Data as JSON: /data/errors/b5de9eb1ce770542.json.
Report an issue: GitHub.