ShouldBindBodyWithJSON : binding:"required" + bool field causes validation error when passing false
โ Bug: binding:"required" fails on bool when value is false ๐ Description When using binding:"required" on a bool field in a Gin handler, passing false causes validation to fail, even though the field is clearly present in the JSON body.
This is unexpected and breaks legitimate use cases where false is a valid, required value.
โ Minimal reproducible example
func ParseRequestBody(c *gin.Context, req any) error {
err := c.ShouldBindBodyWithJSON(req)
if err != nil {
return err
}
return nil
}
type SimpleRequest struct {
Donate bool `json:"donate" binding:"required"`
}
func TestParseRequestBody(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
router.POST("/test", func(c *gin.Context) {
var req SimpleRequest
if err := ParseRequestBody(c, &req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, req)
})
tests := []struct {
name string
requestBody string
expectedCode int
}{
{
name: "donate=false",
requestBody: `{"donate": false}`,
expectedCode: http.StatusOK,
},
{
name: "No donate field is passed",
requestBody: `{}`,
expectedCode: http.StatusBadRequest,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/test", bytes.NewBuffer([]byte(tt.requestBody)))
req.Header.Set("Content-Type", "application/json")
router.ServeHTTP(w, req)
assert.Equal(t, tt.expectedCode, w.Code)
})
}
}
OutPut: (I put some logs) === RUN TestParseRequestBody --- FAIL: TestParseRequestBody (0.02s) === RUN TestParseRequestBody/donate=false 2025-04-16T01:08:27.949+0800 ERROR req/req.go:13 Error parsing request: Key: 'SimpleRequest.Donate' Error:Field validation for 'Donate' failed on the 'required' tag book-management/internal/pkg/req.ParseRequestBody C:/Users/29638/Desktop/book-management/internal/pkg/req/req.go:13 book-management/internal/pkg/req.TestParseRequestBody.func1 C:/Users/29638/Desktop/book-management/internal/pkg/req/req_test.go:22 github.com/gin-gonic/gin.(*Context).Next D:/goproject/pkg/mod/github.com/gin-gonic/[email protected]/context.go:185 github.com/gin-gonic/gin.(*Engine).handleHTTPRequest D:/goproject/pkg/mod/github.com/gin-gonic/[email protected]/gin.go:633 github.com/gin-gonic/gin.(*Engine).ServeHTTP D:/goproject/pkg/mod/github.com/gin-gonic/[email protected]/gin.go:589 book-management/internal/pkg/req.TestParseRequestBody.func2 C:/Users/29638/Desktop/book-management/internal/pkg/req/req_test.go:53 testing.tRunner D:/Scoop/apps/go/1.24.2/src/testing/testing.go:1792 req_test.go:56: Error Trace: C:/Users/29638/Desktop/book-management/internal/pkg/req/req_test.go:56 Error: Not equal: expected: 200 actual : 400 Test: TestParseRequestBody/donate=false Messages: status code should be 200 --- FAIL: TestParseRequestBody/donate=false (0.02s)
=== RUN TestParseRequestBody/No_donate_field_is_passed --- PASS: TestParseRequestBody/No_donate_field_is_passed (0.00s)
FAIL
๐งช Actual Behavior Sending { "donate": false } returns 400 Bad Request
Even though the field is present and valid
โ Expected Behavior Validation should pass when the JSON body includes "donate": false, because: The field is present
The value false is valid for a required bool๐ค
type SimpleRequest struct {
Donate *bool json:"donate" binding:"required"
}
@jiaxin149 Rather than how to parse correctly, I am actually more curious about why this error occurs: "Error parsing request: Key: 'SimpleRequest.Donate' Error:Field validation for 'Donate' failed on the 'required' tag"๐ค
@jiaxin149 When passing true, there was no error โ could it be that false is being treated as a zero value?