{"id":"704c28c5462c6127","repo":"gin-gonic/gin","slug":"can-not-convert-to-map-of-strings","errorCode":null,"errorMessage":"can not convert to map of strings","messagePattern":"can not convert to map of strings","errorType":"validation","errorClass":"ErrConvertToMapString","httpStatus":400,"severity":"error","filePath":"binding/form_mapping.go","lineNumber":29,"sourceCode":"\t\"maps\"\n\t\"mime/multipart\"\n\t\"reflect\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gin-gonic/gin/codec/json\"\n\t\"github.com/gin-gonic/gin/internal/bytesconv\"\n)\n\nvar (\n\terrUnknownType = errors.New(\"unknown type\")\n\n\t// ErrConvertMapStringSlice can not convert to map[string][]string\n\tErrConvertMapStringSlice = errors.New(\"can not convert to map slices of strings\")\n\n\t// ErrConvertToMapString can not convert to map[string]string\n\tErrConvertToMapString = errors.New(\"can not convert to map of strings\")\n)\n\nfunc mapURI(ptr any, m map[string][]string) error {\n\treturn mapFormByTag(ptr, m, \"uri\")\n}\n\nfunc mapForm(ptr any, form map[string][]string) error {\n\treturn mapFormByTag(ptr, form, \"form\")\n}\n\nfunc MapFormWithTag(ptr any, form map[string][]string, tag string) error {\n\treturn mapFormByTag(ptr, form, tag)\n}\n\nvar emptyField = reflect.StructField{}\n\nfunc mapFormByTag(ptr any, form map[string][]string, tag string) error {\n\t// Check if ptr is a map","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/gin-gonic/gin/blob/34dac209ffb6ef85cc78c5d217bbb7ad001d68fd/binding/form_mapping.go#L11-L47","documentation":"ErrConvertToMapString is returned by setFormMap (binding/form_mapping.go:543) when the bind target is a string-keyed map whose element kind is not Slice, but the concrete value fails the map[string]string type assertion. Gin can only write the last form value per key into map[string]string.","triggerScenarios":"Binding into a map whose value type is a non-string scalar (map[string]int, map[string]bool, map[string]time.Time) or a custom alias of map[string]string.","commonSituations":"Binding query params into a typed map (map[string]int) expecting automatic conversion; using a named type `type Params map[string]string` whose assertion to the predeclared map[string]string fails.","solutions":["Use a plain map[string]string as the bind target (Gin will pick the last value for each key).","Switch to a struct with typed fields so the per-field converters run.","For named/aliased map types, bind into map[string]string first, then copy into your alias."],"exampleFix":"// before\ntype Params map[string]string\np := Params{}\nc.ShouldBindWith(&p, binding.Query)\n// after\np := map[string][]string{}\nc.ShouldBindWith(&p, binding.Query)\nparams := Params{}\nfor k, v := range p { params[k] = v[len(v)-1] }","handlingStrategy":"type-guard","validationCode":"var target any = Params{}\nif reflect.TypeOf(target).Kind() == reflect.Map &&\n   reflect.TypeOf(target).Elem().Kind() != reflect.Slice &&\n   reflect.TypeOf(target) != reflect.TypeOf(map[string]string{}) {\n    // will fail; switch to map[string]string or a struct\n}","typeGuard":"func isFormMapString(v any) bool {\n    _, ok := v.(map[string]string)\n    return ok\n}","tryCatchPattern":"if err := c.ShouldBindWith(&m, binding.Form); err != nil {\n    if errors.Is(err, binding.ErrConvertToMapString) {\n        // typed/aliased string-map; bind into map[string]string then copy\n    }\n}","preventionTips":["Avoid named map types as bind targets; use the predeclared map[string]string.","Convert form values to typed maps after binding, not during.","Add a unit test that binds a sample request into each of your map targets."],"tags":["binding","form","map","go"],"analyzedSha":"34dac209ffb6ef85cc78c5d217bbb7ad001d68fd","analyzedAt":"2026-08-04T21:26:18.438Z","schemaVersion":2}