gin-gonic/gin · error · errUnknownType
unknown type
Error message
unknown type
What it means
Returned by setWithProperType's default branch (binding/form_mapping.go:385) when Gin's reflection-driven form/URI binder encounters a struct field whose reflect.Kind it cannot handle. The package declares this sentinel only for genuinely unsupported scalar kinds (complex64/128, chan, func, unsafe.Pointer, interface) since the explicit switch covers int/uint/bool/float/string/struct(time,multipart.FileHeader)/map/ptr.
Source
Thrown at binding/form_mapping.go:23
package binding
import (
"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)View on GitHub (pinned to 34dac209ff)
Solutions
- Inspect the struct: find fields typed complex*, chan, func, interface, or unsafe.Pointer and remove them from the binding DTO (or mark them with the tag `form:"-"` / `uri:"-"`).
- Move non-bindable fields out of the request struct into a separate domain type populated after binding.
- If you need an interface field, wrap it in a concrete type that implements encoding.TextUnmarshaler / binding.BindUnmarshaler so trySetCustom handles it before the default branch.
Example fix
// before
type Req struct {
Handler func() `form:"handler"`
Value complex128 `form:"value"`
}
// after
type Req struct {
Value string `form:"value"`
} Defensive patterns
Strategy: validation
Validate before calling
func isBindableStruct(t reflect.Type) error {
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Tag.Get("form") == "-" && f.Tag.Get("uri") == "-" { continue }
switch f.Type.Kind() {
case reflect.Chan, reflect.Func, reflect.Complex64, reflect.Complex128, reflect.UnsafePointer:
return fmt.Errorf("field %s has unsupported kind %s", f.Name, f.Type.Kind())
}
}
return nil
}
// at startup: if err := isBindableStruct(reflect.TypeOf(Req{})); err != nil { log.Fatal(err) } Type guard
func isBindableKind(k reflect.Kind) bool {
switch k {
case reflect.Chan, reflect.Func, reflect.Complex64, reflect.Complex128,
reflect.UnsafePointer, reflect.Interface:
return false
}
return true
} Prevention
- Keep request DTOs as plain structs of bindable scalar kinds (int, uint, bool, float, string, time.Time, multipart.FileHeader, maps, slices, pointers).
- Mark any non-bindable field with `form:"-" uri:"-"` so the binder skips it.
- Add a startup reflect-based sanity check over your binding structs in tests.
When it happens
Trigger: Calling c.ShouldBindWith / c.ShouldBindUri / c.ShouldBindQuery on a struct that has a field of kind complex64, complex128, chan, func, interface, or unsafe.Pointer, with a matching form/uri/query key present in the request.
Common situations: Defining a request DTO with a func field or chan field by mistake; embedding an interface-typed field expecting Gin to deserialize it; copying a domain model that contains non-POD fields into a binding struct.
Related errors
- can not convert to map slices of strings
- can not convert to map of strings
- %s is not supported in the collection_format. (multi, csv, s
- %q is not valid value for %s
- invalid request
AI-assisted analysis of gin-gonic/gin@34dac209ff (2026-08-04).
Data as JSON: /data/errors/22cd985fdcf2e7ec.json.
Report an issue: GitHub.