ent/ent · error
StorageKey.Table is allowed only for M2M edges (got %s)
Error message
StorageKey.Table is allowed only for M2M edges (got %s)
What it means
In setStorageKey, a custom StorageKey annotation with a Table is only valid for many-to-many edges, because only M2M relations introduce a join table. Ent throws this when a Table is set on an edge whose relation type is not M2M.
Source
Thrown at entc/gen/type.go:2114
}
// OrderFieldName returns the function/option name for ordering by edge field.
func (e Edge) OrderFieldName() (string, error) {
if !e.Unique {
return "", fmt.Errorf("edge %q is not-unique", e.Name)
}
return fmt.Sprintf("By%sField", pascal(e.Name)), nil
}
// setStorageKey sets the storage-key option in the schema or fail.
func (e *Edge) setStorageKey() error {
key, err := e.StorageKey()
if err != nil || key == nil {
return err
}
switch rel := e.Rel; {
case key.Table != "" && rel.Type != M2M:
return fmt.Errorf("StorageKey.Table is allowed only for M2M edges (got %s)", e.Rel.Type)
case len(key.Columns) == 1 && rel.Type == M2M:
return fmt.Errorf("%s edge have 2 columns. Use edge.Columns(to, from) instead", e.Rel.Type)
case len(key.Columns) > 1 && rel.Type != M2M:
return fmt.Errorf("%s edge does not have 2 columns. Use edge.Column(%s) instead", e.Rel.Type, key.Columns[0])
}
if key.Table != "" {
e.Rel.Table = key.Table
}
if len(key.Columns) > 0 {
e.Rel.Columns[0] = key.Columns[0]
}
if len(key.Columns) > 1 {
e.Rel.Columns[1] = key.Columns[1]
}
return nil
}
// StorageKey returns the storage-key defined on the schema if exists.View on GitHub (pinned to 69d5d4deb1)
Solutions
- Remove the Table field from the StorageKey annotation on non-M2M edges
- Move the Table annotation to the actual M2M edge
- If renaming the FK column is the goal, use Column(...) in the annotation instead
Example fix
// before
edge.To("posts", Post.Type).StorageKey(entsql.Annotation{Table: "user_posts"})
// after
edge.To("posts", Post.Type).StorageKey(entsql.Annotation{Column: "user_id"}) Defensive patterns
Strategy: validation
Validate before calling
ann := entsql.Annotation{Table: "user_posts"}
if ann.Table != "" && edge.Rel.Type != gen.M2M {
return fmt.Errorf("Table only allowed on M2M edges")
} Try / catch
if err := edge.SetStorageKey(); err != nil {
return fmt.Errorf("storage-key config invalid: %w", err)
} Prevention
- Only annotate join tables on M2M edges
- Use Column(s), not Table, for FK naming on non-M2M edges
- Review entsql annotations when changing relation cardinality
When it happens
Trigger: Annotating an O2O, O2M or inverse edge with entsql.Annotation{Table: "..."} via .StorageKey(an) where the relation type resolves to O2O/O2M/M2O.
Common situations: Copy-pasting a join-table StorageKey from an M2M edge to a one-to-many edge; renaming a FK table on the wrong edge type; misunderstanding that non-M2M edges use the owner's table.
Related errors
- %s edge have 2 columns. Use edge.Columns(to, from) instead
- %s edge does not have 2 columns. Use edge.Column(%s) instead
- multiple storage-keys defined for edge %q<->%q
- foreign-key was not found for edge %q of type %s
- edge %q is unique
AI-assisted analysis of ent/ent@69d5d4deb1 (2026-09-03).
Data as JSON: /api/errors/a155a410417df487.
Report an issue: GitHub.