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

  1. Move the named type onto a struct model as a field with a serializer or Valuer/Scanner.
  2. Constrain generics with `T any` struct requirements or a compile-time `struct{ _ T }` assertion.
  3. 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

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


AI-assisted analysis of go-gorm/gorm@1d6ce99528 (2026-08-15). Data as JSON: /api/errors/bb3871dd3ead0f20. Report an issue: GitHub.