go-gorm/gorm · error · ErrUnsupportedDataType
unsupported data type: %s.%s
Error message
unsupported data type: %s.%s
What it means
Same non-struct destination rejection as its sibling, but for named types: the destination resolves to a non-struct kind with a non-empty PkgPath, so the error carries `pkgpath.TypeName` instead of the raw value — more actionable for user-defined types like `type Status int` or `type IDs []int`.
Source
Thrown at schema/schema.go:162
modelType := reflect.ValueOf(dest).Type()
if modelType.Kind() == reflect.Ptr {
modelType = modelType.Elem()
}
if modelType.Kind() != reflect.Struct {
if modelType.Kind() == reflect.Interface {
modelType = reflect.Indirect(reflect.ValueOf(dest)).Elem().Type()
}
for modelType.Kind() == reflect.Slice || modelType.Kind() == reflect.Array || modelType.Kind() == reflect.Ptr {
modelType = modelType.Elem()
}
if modelType.Kind() != reflect.Struct {
if modelType.PkgPath() == "" {
return nil, fmt.Errorf("%w: %+v", ErrUnsupportedDataType, dest)
}
return nil, fmt.Errorf("%w: %s.%s", ErrUnsupportedDataType, modelType.PkgPath(), modelType.Name())
}
}
// Cache the Schema for performance,
// Use the modelType or modelType + schemaTable (if it present) as cache key.
var schemaCacheKey interface{} = modelType
if specialTableName != "" {
schemaCacheKey = fmt.Sprintf("%p-%s", modelType, specialTableName)
}
// Load exist schema cache, return if exists
if v, ok := cacheStore.Load(schemaCacheKey); ok {
s := v.(*Schema)
// Wait for the initialization of other goroutines to complete
<-s.initialized
return s, s.err
}
View on GitHub (pinned to 1d6ce99528)
Solutions
- Move the named type onto a struct model as a field with a serializer or Valuer/Scanner.
- Constrain generics with `T any` struct requirements or a compile-time `struct{ _ T }` assertion.
- Read the pkg.Type in the message to identify exactly which type leaked in.
Example fix
// before
type IDs []uint
err := db.AutoMigrate(&IDs{}) // non-struct named type
// after
type Record struct {
ID uint `gorm:"primaryKey"`
IDs IDs `gorm:"serializer:json"`
}
err := db.AutoMigrate(&Record{}) Defensive patterns
Strategy: type-guard
Validate before calling
// For dynamically sourced models, verify named struct kind
func isNamedStruct(dest any) bool {
t := reflect.TypeOf(dest)
for t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice || t.Kind() == reflect.Array { t = t.Elem() }
return t.Kind() == reflect.Struct && t.PkgPath() != "" || t.Kind() == reflect.Struct
} Type guard
func isModelType(t reflect.Type) bool {
for t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice || t.Kind() == reflect.Array { t = t.Elem() }
return t.Kind() == reflect.Struct
} Try / catch
if err := db.AutoMigrate(dest).Error; err != nil {
if errors.Is(err, schema.ErrUnsupportedDataType) {
return fmt.Errorf("%T is a named non-struct type; wrap it in a struct model", dest)
}
return err
} Prevention
- Wrap scalar aliases as fields on real models with serializers.
- Add compile-time struct constraints in generic repositories.
- The pkg.Type in the message names the culprit — audit that type's usage.
When it happens
Trigger: AutoMigrate or Find on a named non-struct type: `type IDs []uint` then `db.AutoMigrate(IDs{})`; named map types (`type Meta map[string]any`) without Valuer/Scanner used as the destination.
Common situations: Custom scalar aliases mistaken for models; generic repositories where T is unconstrained and occasionally instantiated with a scalar; migrating code from ORMs that allowed primitive destinations.
Related errors
- failed to parse field: %s, error: %w
- unsupported data type: %+v
- violates check constraint
- unsupported relationship
- record not found
AI-assisted analysis of go-gorm/gorm@1d6ce99528 (2026-08-15).
Data as JSON: /api/errors/bb3871dd3ead0f20.
Report an issue: GitHub.