gin-gonic/gin · error
invalid request
Error message
invalid request
What it means
Returned by jsonBinding.Bind (binding/json.go:35) when the supplied *http.Request is nil OR its Body is nil. The JSON decoder needs a non-nil io.Reader, so Gin refuses before attempting to read. This only happens when the JSON binding is invoked directly with a malformed request object, not during normal c.JSON-style handlers.
Source
Thrown at binding/json.go:35
// Decoder instance. UseNumber causes the Decoder to unmarshal a number into an
// any as a Number instead of as a float64.
var EnableDecoderUseNumber = false
// EnableDecoderDisallowUnknownFields is used to call the DisallowUnknownFields method
// on the JSON Decoder instance. DisallowUnknownFields causes the Decoder to
// return an error when the destination is a struct and the input contains object
// keys which do not match any non-ignored, exported fields in the destination.
var EnableDecoderDisallowUnknownFields = false
type jsonBinding struct{}
func (jsonBinding) Name() string {
return "json"
}
func (jsonBinding) Bind(req *http.Request, obj any) error {
if req == nil || req.Body == nil {
return errors.New("invalid request")
}
return decodeJSON(req.Body, obj)
}
func (jsonBinding) BindBody(body []byte, obj any) error {
return decodeJSON(bytes.NewReader(body), obj)
}
func decodeJSON(r io.Reader, obj any) error {
decoder := json.API.NewDecoder(r)
if EnableDecoderUseNumber {
decoder.UseNumber()
}
if EnableDecoderDisallowUnknownFields {
decoder.DisallowUnknownFields()
}
if err := decoder.Decode(obj); err != nil {
return errView on GitHub (pinned to 34dac209ff)
Solutions
- Ensure the handler reads the body at most once; if you must re-read, reset it via io.NopCloser(bytes.NewBuffer(raw)) before the second bind.
- In tests, build the request with http.NewRequest(http.MethodPost, url, strings.NewReader(json)) so Body is non-nil.
- Guard the handler: if c.Request == nil || c.Request.Body == nil, short-circuit with a 400 before calling ShouldBindJSON.
Example fix
// before raw, _ := io.ReadAll(c.Request.Body) var a A; binding.JSON.Bind(c.Request, &a) // Body now consumed/nil // after raw, _ := io.ReadAll(c.Request.Body) c.Request.Body = io.NopCloser(bytes.NewBuffer(raw)) var a A; c.ShouldBindJSON(&a)
Defensive patterns
Strategy: validation
Validate before calling
if req == nil || req.Body == nil {
return errors.New("request or body is nil; cannot bind JSON")
}
return binding.JSON.Bind(req, obj) Try / catch
if err := binding.JSON.Bind(req, &obj); err != nil {
if err.Error() == "invalid request" {
// req or body was nil; reset body and retry, or return 400
}
} Prevention
- Never read c.Request.Body twice; reset it via io.NopCloser(bytes.NewBuffer(raw)) if you must.
- Build requests in tests with http.NewRequest so Body is always set.
- Centralise body reading in one middleware/handler per request.
When it happens
Trigger: Calling binding.JSON.Bind(req, &obj) with req == nil; calling it on an http.Request whose Body was already consumed and not reset (Body becomes nil after Close); constructing an http.Request by hand without setting Body.
Common situations: Re-binding the same request twice (first read closes the body); unit tests that build *http.Request without http.NewRequest; middleware that sets c.Request.Body = nil.
Related errors
- unknown type
- can not convert to map slices of strings
- can not convert to map of strings
- unsupported field type for multipart.FileHeader
- unsupported len of array for []*multipart.FileHeader
AI-assisted analysis of gin-gonic/gin@34dac209ff (2026-08-04).
Data as JSON: /data/errors/f166cb73b15ab87f.json.
Report an issue: GitHub.