go-gorm/gorm · error
invalid field found for struct %v's field %s: define a valid
Error message
invalid field found for struct %v's field %s: define a valid foreign key for relations or implement the Valuer/Scanner interface
What it means
GORM's relation guesser walks a fallback chain (guess → belongs → embedded-belongs → has → embedded-has). If every strategy fails to find usable foreign keys, the final default branch reports that the field is invalid: either define an explicit foreign key or make the type implement driver.Valuer/sql.Scanner. It means the field looks like a relation but no convention-based key could be matched.
Source
Thrown at schema/relationship.go:483
gl = guessBelongs
} else {
gl = guessHas
}
}
reguessOrErr := func() {
switch cgl {
case guessGuess:
schema.guessRelation(relation, field, guessBelongs)
case guessBelongs:
schema.guessRelation(relation, field, guessEmbeddedBelongs)
case guessEmbeddedBelongs:
schema.guessRelation(relation, field, guessHas)
case guessHas:
schema.guessRelation(relation, field, guessEmbeddedHas)
// case guessEmbeddedHas:
default:
schema.err = fmt.Errorf("invalid field found for struct %v's field %s: define a valid foreign key for relations or implement the Valuer/Scanner interface",
schema, field.Name)
}
}
switch gl {
case guessBelongs:
primarySchema, foreignSchema = relation.FieldSchema, schema
case guessEmbeddedBelongs:
if field.OwnerSchema == nil {
reguessOrErr()
return
}
primarySchema, foreignSchema = relation.FieldSchema, field.OwnerSchema
case guessHas:
case guessEmbeddedHas:
if field.OwnerSchema == nil {
reguessOrErr()
returnView on GitHub (pinned to 1d6ce99528)
Solutions
- Add an explicit `gorm:"foreignKey:..."` (and references if needed) naming real columns.
- If the struct is data, implement driver.Valuer and sql.Scanner on it.
- Or serialize it: `gorm:"serializer:json"` / `serializer:gob`.
- Or embed with `gorm:"embedded"` to flatten its fields into the parent table instead of a relation.
Example fix
// before
type Order struct {
ID uint
Money Money // struct, no matching FK convention
}
// after
type Order struct {
ID uint
Money Money `gorm:"serializer:json"`
} Defensive patterns
Strategy: type-guard
Validate before calling
// Detect fields that look like relations but have no key convention match
func checkModel(t reflect.Type) error {
for t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice { t = t.Elem() }
if t.Kind() != reflect.Struct { return nil }
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
ft := f.Type
for ft.Kind() == reflect.Ptr || ft.Kind() == reflect.Slice { ft = ft.Elem() }
if ft.Kind() == reflect.Struct && !isTimeOrSQL(ft) {
if !strings.Contains(f.Tag.Get("gorm"), "foreignKey") &&
!strings.Contains(f.Tag.Get("gorm"), "serializer") &&
!strings.Contains(f.Tag.Get("gorm"), "embedded") {
return fmt.Errorf("field %s needs foreignKey/serializer/embedded/- tag", f.Name)
}
}
}
return nil
} Type guard
func implementsValuerScanner(t reflect.Type) bool {
v := reflect.New(t).Elem().Interface()
_, okV := v.(driver.Valuer)
_, okS := any(&[1]driver.Valuer{}).(*sql.Scanner) // placeholder; real check below
return okV || okS
} Try / catch
if err := db.AutoMigrate(&Order{}); err != nil {
if strings.Contains(err.Error(), "define a valid foreign key") {
return fmt.Errorf("add foreignKey tag, serializer, or Valuer/Scanner to the offending field: %w", err)
}
return err
} Prevention
- Standardize value-object fields with serializer:json from day one.
- Add explicit foreignKey tags when renaming away from ID conventions.
- Use gorm:"embedded" for flattening value structs.
When it happens
Trigger: A struct-typed field whose type has no ID-style fields matching any guessing convention and no explicit foreignKey tag; embedded structs without owner schema context (field.OwnerSchema == nil in the embedded branches); custom struct types that are data, not relations, yet lack Valuer/Scanner.
Common situations: Value objects (Money, Address) stored inline without serializers; renaming ID fields so conventions like <Owner>Name no longer match; embedding third-party structs GORM cannot introspect.
Related errors
- failed to parse field: %s, error: %w
- unsupported data type %v for %v on field %s
- invalid polymorphic type %v for %v on field %s, missing fiel
- invalid polymorphic foreign keys %+v for %v on field %s
- invalid polymorphic type %v for %v on field %s, missing prim
AI-assisted analysis of go-gorm/gorm@1d6ce99528 (2026-08-15).
Data as JSON: /api/errors/e897fe26baf52fb9.
Report an issue: GitHub.