go-gorm/gorm · error

failed to get schema

Error message

failed to get schema

What it means

During AutoMigrate, GORM runs RunWithValue to parse the model into a *schema.Statement. If stmt.Schema is nil after parsing, it returns errors.New("failed to get schema"). A nil schema means the value you passed could not be interpreted as a model struct at all, so migration cannot proceed for it.

Source

Thrown at migrator/migrator.go:133

		queryTx.DryRun = false
		execTx = m.DB.Session(&gorm.Session{Logger: &printSQLLogger{Interface: m.DB.Logger}})
	}
	return queryTx, execTx
}

// AutoMigrate auto migrate values
func (m Migrator) AutoMigrate(values ...interface{}) error {
	for _, value := range m.ReorderModels(values, true) {
		queryTx, execTx := m.GetQueryAndExecTx()
		if !queryTx.Migrator().HasTable(value) {
			if err := execTx.Migrator().CreateTable(value); err != nil {
				return err
			}
		} else {
			if err := m.RunWithValue(value, func(stmt *gorm.Statement) error {

				if stmt.Schema == nil {
					return errors.New("failed to get schema")
				}

				columnTypes, err := queryTx.Migrator().ColumnTypes(value)
				if err != nil {
					return err
				}
				var (
					parseIndexes          = stmt.Schema.ParseIndexes()
					parseCheckConstraints = stmt.Schema.ParseCheckConstraints()
				)
				for _, dbName := range stmt.Schema.DBNames {
					var foundColumn gorm.ColumnType

					for _, columnType := range columnTypes {
						if columnType.Name() == dbName {
							foundColumn = columnType
							break
						}

View on GitHub (pinned to 1d6ce99528)

Solutions

  1. Pass a pointer to a plain struct: db.AutoMigrate(&User{}, &Order{}).
  2. Remove nil values from the slice of models before calling AutoMigrate.
  3. If migrating by table name was intended, use the Migrator SQL APIs (HasTable/CreateTable with a model) instead - AutoMigrate needs a model type.
  4. Log reflect.TypeOf(value) for each value to find the offending entry.

Example fix

// before
db.AutoMigrate("users", &User{})

// after
db.AutoMigrate(&User{})
Defensive patterns

Strategy: validation

Validate before calling

func validateModels(values ...interface{}) error {
    for _, v := range values {
        rv := reflect.Indirect(reflect.ValueOf(v))
        if !rv.IsValid() || rv.Kind() != reflect.Struct {
            return fmt.Errorf("automigrate: %T is not a struct pointer", v)
        }
    }
    return nil
}
// call before db.AutoMigrate(...)

Type guard

func isStructPointer(v interface{}) bool {
    rv := reflect.ValueOf(v)
    return rv.Kind() == reflect.Ptr && rv.Elem().Kind() == reflect.Struct
}

Try / catch

if err := db.AutoMigrate(models...); err != nil {
    if strings.Contains(err.Error(), "failed to get schema") {
        log.Printf("automigrate got a non-struct model; got %v", models)
    }
    return err
}

Prevention

When it happens

Trigger: Calling db.AutoMigrate with a value that schema.Parse cannot handle: a non-struct (string table name, map, int), a nil pointer, a pointer-to-pointer, or a struct whose fields all fail to parse. ReorderModels only reorders; it does not validate.

Common situations: Passing &[]User{} or User{} where a *User was expected; passing a table name string ('users') instead of a model; passing an interface{} holding nil from dynamic registration code; refactoring models so an embedded type became invalid.

Related errors


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