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 err

View on GitHub (pinned to 34dac209ff)

Solutions

  1. 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.
  2. In tests, build the request with http.NewRequest(http.MethodPost, url, strings.NewReader(json)) so Body is non-nil.
  3. 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

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


AI-assisted analysis of gin-gonic/gin@34dac209ff (2026-08-04). Data as JSON: /data/errors/f166cb73b15ab87f.json. Report an issue: GitHub.