gin-gonic/gin · error · ErrConvertMapStringSlice

can not convert to map slices of strings

Error message

can not convert to map slices of strings

What it means

ErrConvertMapStringSlice is returned by setFormMap (binding/form_mapping.go:534) when the binding target is a map whose element kind is Slice but the concrete value is not exactly map[string][]string. Gin only knows how to copy form values into the canonical map[string][]string shape.

Source

Thrown at binding/form_mapping.go:26

	"encoding"
	"errors"
	"fmt"
	"maps"
	"mime/multipart"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin/codec/json"
	"github.com/gin-gonic/gin/internal/bytesconv"
)

var (
	errUnknownType = errors.New("unknown type")

	// ErrConvertMapStringSlice can not convert to map[string][]string
	ErrConvertMapStringSlice = errors.New("can not convert to map slices of strings")

	// ErrConvertToMapString can not convert to map[string]string
	ErrConvertToMapString = errors.New("can not convert to map of strings")
)

func mapURI(ptr any, m map[string][]string) error {
	return mapFormByTag(ptr, m, "uri")
}

func mapForm(ptr any, form map[string][]string) error {
	return mapFormByTag(ptr, form, "form")
}

func MapFormWithTag(ptr any, form map[string][]string, tag string) error {
	return mapFormByTag(ptr, form, tag)
}

var emptyField = reflect.StructField{}

View on GitHub (pinned to 34dac209ff)

Solutions

  1. Declare the bind target as map[string][]string (the only slice-map shape Gin supports) and convert values yourself afterwards.
  2. Prefer a struct with typed slice fields (e.g. IDs []int `form:"ids"`) so Gin's per-element setWithProperType does the conversion.
  3. If you need a custom map type, build a map[string][]string, call MapFormWithTag into it, then copy into your custom map.

Example fix

// before
m := map[string][]int{}
c.ShouldBindWith(&m, binding.Query)
// after
raw := map[string][]string{}
c.ShouldBindWith(&raw, binding.Query)
// then convert raw into your typed map
Defensive patterns

Strategy: type-guard

Validate before calling

var target any = map[string][]int{}
if reflect.TypeOf(target).Kind() == reflect.Map {
    elem := reflect.TypeOf(target).Elem()
    if elem.Kind() == reflect.Slice && elem != reflect.TypeOf([]string{}).Elem() {
        // will fail; switch to map[string][]string or use a struct
    }
}

Type guard

func isFormMapStringSlice(v any) bool {
    _, ok := v.(map[string][]string)
    return ok
}

Try / catch

if err := c.ShouldBindWith(&m, binding.Form); err != nil {
    if errors.Is(err, binding.ErrConvertMapStringSlice) {
        // target was a non-canonical slice-map; bind into map[string][]string and convert
    }
}

Prevention

When it happens

Trigger: Binding a request into a variable typed map[string][]<non-string> (e.g. map[string][]int, map[string][]MyType) and then calling c.ShouldBind / MapFormWithTag.

Common situations: Trying to reuse a typed map (map[string][]int) as the bind target instead of a struct; passing a custom map alias type that does not satisfy the concrete map[string][]string assertion.

Related errors


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