Description

A regression we noticed on attempting to update to v1.11.0 from v1.10.1, specifically in binding/form_mapping.go setByForm().

Previously the following code would execute in tests, now it panics:

// You can edit this code!
// Click here and start typing.
package main

import (
    "fmt"
    "net/http"
    "net/http/httptest"
    "net/url"
    "strconv"

    "github.com/gin-gonic/gin"
)

type ReportCreateRequest struct {
    AccountID string   `form:"account_id" json:"account_id" xml:"account_id"`
    StatusIDs []string `form:"status_ids[]" json:"status_ids" xml:"status_ids"`
    Comment   string   `form:"comment" json:"comment" xml:"comment"`
    Forward   bool     `form:"forward" json:"forward" xml:"forward"`
    Category  string   `form:"category" json:"category" xml:"category"`
    RuleIDs   []string `form:"rule_ids[]" json:"rule_ids" xml:"rule_ids"`
}

func setForm(c *gin.Context, form *ReportCreateRequest) {
    c.Request.Form = url.Values{
        "account_id":   {form.AccountID},
        "status_ids[]": form.StatusIDs,
        "comment":      {form.Comment},
        "forward":      {strconv.FormatBool(form.Forward)},
        "category":     {form.Category},
        "rule_ids[]":   form.RuleIDs,
    }
}

func main() {
    for _, test := range []*ReportCreateRequest{
        {
            AccountID: "some account id",
            StatusIDs: []string{},
            Comment:   "",
            Forward:   false,
        },
    } {
        rw := httptest.NewRecorder()
        c, _ := gin.CreateTestContext(rw)
        c.Request = new(http.Request)

        setForm(c, test)

        var form ReportCreateRequest
        err := c.ShouldBind(&form)
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Printf("success: %+v\n", form)
        }
    }
}

which produces the following output if run:

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

panic: runtime error: index out of range [0] with length 0

goroutine 1 [running]:
github.com/gin-gonic/gin/binding.setByForm({0xdaff60?, 0xc0000f4da0?, 0xdc54cc?}, {{0xdc54bd, 0x9}, {0x0, 0x0}, {0x109f370, 0xdaff60}, {0xdc54c7, ...}, ...}, ...)
    github.com/gin-gonic/gin@v1.11.0/binding/form_mapping.go:243 +0x792
github.com/gin-gonic/gin/binding.formSource.TrySet(0x41c4e6?, {0xdaff60?, 0xc0000f4da0?, 0xdc54cd?}, {{0xdc54bd, 0x9}, {0x0, 0x0}, {0x109f370, 0xdaff60}, ...}, ...)
    github.com/gin-gonic/gin@v1.11.0/binding/form_mapping.go:74 +0x87
github.com/gin-gonic/gin/binding.tryToSetValue({0xdaff60?, 0xc0000f4da0?, 0x4c33cf?}, {{0xdc54bd, 0x9}, {0x0, 0x0}, {0x109f370, 0xdaff60}, {0xdc54c7, ...}, ...}, ...)
    github.com/gin-gonic/gin@v1.11.0/binding/form_mapping.go:173 +0x391
github.com/gin-gonic/gin/binding.mapping({0xdaff60?, 0xc0000f4da0?, 0x3?}, {{0xdc54bd, 0x9}, {0x0, 0x0}, {0x109f370, 0xdaff60}, {0xdc54c7, ...}, ...}, ...)
    github.com/gin-gonic/gin@v1.11.0/binding/form_mapping.go:107 +0x148
github.com/gin-gonic/gin/binding.mapping({0xe81900?, 0xc0000f4d90?, 0xc0000e7b70?}, {{0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, ...}, ...}, ...)
    github.com/gin-gonic/gin@v1.11.0/binding/form_mapping.go:125 +0x498
github.com/gin-gonic/gin/binding.mapping({0xd9e040?, 0xc0000f4d90?, 0x4c4689?}, {{0x0, 0x0}, {0x0, 0x0}, {0x0, 0x0}, {0x0, ...}, ...}, ...)
    github.com/gin-gonic/gin@v1.11.0/binding/form_mapping.go:96 +0x2e5
github.com/gin-gonic/gin/binding.mappingByPtr({0xd9e040?, 0xc0000f4d90?}, {0x108b820?, 0xc000238540?}, {0xed8b2d?, 0x16db080?})
    github.com/gin-gonic/gin@v1.11.0/binding/form_mapping.go:78 +0xed
github.com/gin-gonic/gin/binding.mapFormByTag({0xd9e040, 0xc0000f4d90?}, 0xc000238540, {0xed8b2d, 0x4})
    github.com/gin-gonic/gin@v1.11.0/binding/form_mapping.go:60 +0x1e8
github.com/gin-gonic/gin/binding.mapForm(...)
    github.com/gin-gonic/gin@v1.11.0/binding/form_mapping.go:35
github.com/gin-gonic/gin/binding.formBinding.Bind({}, 0xc0001c6500, {0xd9e040, 0xc0000f4d90})
    github.com/gin-gonic/gin@v1.11.0/binding/form.go:31 +0x90
github.com/gin-gonic/gin.(*Context).ShouldBindWith(...)
    github.com/gin-gonic/gin@v1.11.0/context.go:870
github.com/gin-gonic/gin.(*Context).ShouldBind(0xc0001fc100, {0xd9e040, 0xc0000f4d90})
    github.com/gin-gonic/gin@v1.11.0/context.go:820 +0x34c
main.main()
    tmp/main.go:51 +0x539
exit status 2

Gin Version

v1.11.0

Can you reproduce the bug?

Yes

Source Code

No response

Go Version

go1.25

Operating System

No response