go-gorm/gorm · error
invalid foreign key: %s
Error message
invalid foreign key: %s
What it means
While building a many-to-many relation, GORM validates explicit `foreignKey` tag values against the owner schema via LookUpField. Each named column must exist on the declaring struct; the first unknown name aborts with 'invalid foreign key: %s'. This is the own-side (owner) validation; the references side has its own check.
Source
Thrown at schema/relationship.go:293
err error
joinTableFields []reflect.StructField
fieldsMap = map[string]*Field{}
ownFieldsMap = map[string]*Field{} // fix self join many2many
referFieldsMap = map[string]*Field{}
joinForeignKeys = toColumns(field.TagSettings["JOINFOREIGNKEY"])
joinReferences = toColumns(field.TagSettings["JOINREFERENCES"])
)
ownForeignFields := schema.PrimaryFields
refForeignFields := relation.FieldSchema.PrimaryFields
if len(relation.foreignKeys) > 0 {
ownForeignFields = []*Field{}
for _, foreignKey := range relation.foreignKeys {
if field := schema.LookUpField(foreignKey); field != nil {
ownForeignFields = append(ownForeignFields, field)
} else {
schema.err = fmt.Errorf("invalid foreign key: %s", foreignKey)
return
}
}
}
if len(relation.primaryKeys) > 0 {
refForeignFields = []*Field{}
for _, foreignKey := range relation.primaryKeys {
if field := relation.FieldSchema.LookUpField(foreignKey); field != nil {
refForeignFields = append(refForeignFields, field)
} else {
schema.err = fmt.Errorf("invalid foreign key: %s", foreignKey)
return
}
}
}
for idx, ownField := range ownForeignFields {View on GitHub (pinned to 1d6ce99528)
Solutions
- Correct the foreignKeys value to a field that exists on the declaring struct.
- Drop the foreignKeys tag to let GORM default to the schema's primary fields.
- If the key is embedded, use the exported field name reachable by LookUpField.
Example fix
// before
type Tag struct {
ID uint
Members []User `gorm:"many2many:user_tags;foreignKey:TagUID"`
}
// after
type Tag struct {
ID uint
TagUID string
Members []User `gorm:"many2many:user_tags;foreignKey:TagUID"`
} Defensive patterns
Strategy: validation
Validate before calling
// Validate many2many foreignKeys resolve on the declaring struct
func fieldExists(model any, name string) bool {
t := reflect.TypeOf(model)
for t.Kind() == reflect.Ptr || t.Kind() == reflect.Slice { t = t.Elem() }
_, ok := t.FieldByName(name)
return ok
}
if !fieldExists(&Tag{}, "TagUID") { panic("foreignKey TagUID missing on Tag") } Try / catch
if err := db.AutoMigrate(&Tag{}); err != nil {
if strings.Contains(err.Error(), "invalid foreign key") {
return fmt.Errorf("foreignKeys tag must name a field on the declaring model: %w", err)
}
return err
} Prevention
- Remember: foreignKeys names a column on the DECLARING model, references on the RELATED model.
- Rerun tag checks after renaming model fields (IDE rename does not update tag strings).
- Default to omitting the tags; add them only for non-default keys.
When it happens
Trigger: `Members []User `gorm:"many2many:user_tags;foreignKey:UserRef"`` where the declaring struct has no field resolving to UserRef. Also triggered by case mismatches or names that only exist on the join table, not on the model.
Common situations: Renaming model fields during refactor without updating relation tags; pointing foreignKeys at join-table columns; typos or stale names after merging models.
Related errors
- invalid polymorphic foreign keys %+v for %v on field %s
- failed to assign association %#v, make sure foreign fields e
- 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
AI-assisted analysis of go-gorm/gorm@1d6ce99528 (2026-08-15).
Data as JSON: /api/errors/7059e7b868643d7b.
Report an issue: GitHub.