ent/ent · error
more than 1 table for batch insert: %q != %q
Error message
more than 1 table for batch insert: %q != %q
What it means
ent's batch creator requires all nodes in one sqlgraph.BatchCreateSpec to target the same table. While iterating c.Nodes it compares each node's table with the previous one; any mismatch means a single multi-row INSERT statement would span different tables, which is impossible, so it reports the two conflicting table names.
Source
Thrown at dialect/sql/sqlgraph/graph.go:1516
}
s.Set(c.ID.Column, sql.Expr(fmt.Sprintf("LAST_INSERT_ID(%s)", s.Table().C(c.ID.Column))))
}))
}
type batchCreator struct {
graph
*BatchCreateSpec
}
func (c *batchCreator) nodes(ctx context.Context, drv dialect.Driver) error {
if len(c.Nodes) == 0 {
return nil
}
columns := make(map[string]struct{})
values := make([]map[string]driver.Value, len(c.Nodes))
for i, node := range c.Nodes {
if i > 0 && node.Table != c.Nodes[i-1].Table {
return fmt.Errorf("more than 1 table for batch insert: %q != %q", node.Table, c.Nodes[i-1].Table)
}
values[i] = make(map[string]driver.Value)
if node.ID != nil && node.ID.Value != nil {
columns[node.ID.Column] = struct{}{}
values[i][node.ID.Column] = node.ID.Value
}
edges := EdgeSpecs(node.Edges).GroupRel()
err := setTableColumns(node.Fields, edges, func(column string, value driver.Value) {
columns[column] = struct{}{}
values[i][column] = value
})
if err != nil {
return err
}
}
for column := range columns {
for i := range values {
if _, exists := values[i][column]; !exists {View on GitHub (pinned to 69d5d4deb1)
Solutions
- Group nodes by table and issue one sqlgraph batch insert per table.
- If using generated code, ensure each entity type's CreateBulk is used on its own client (client.Foo.CreateBulk vs client.Bar.CreateBulk).
- Fix custom spec-assembly code so a BatchCreateSpec never mixes NodeSpec.Table values.
Example fix
// before: one spec with mixed tables
spec.Nodes = append(spec.Nodes, fooNode, barNode)
// after
fooSpec.Nodes = []sqlgraph.NodeSpec{fooNode}
barSpec.Nodes = []sqlgraph.NodeSpec{barNode} Defensive patterns
Strategy: validation
Validate before calling
tables := map[string]struct{}{}
for _, n := range spec.Nodes { tables[n.Table] = struct{}{} }
if len(tables) > 1 { return fmt.Errorf("batch must target a single table") } Try / catch
if err := batchCreate(ctx, nodes); err != nil {
if strings.Contains(err.Error(), "more than 1 table") { /* split nodes per table */ }
} Prevention
- Only batch entities of the same type via one client's CreateBulk
- Never merge NodeSpecs of different tables into one BatchCreateSpec
- Type-generic helpers should key batches by entity type
When it happens
Trigger: Calling client.Foo.CreateBulk(...) where the builder list mixes entity types/tables — typically only possible via custom code that appends heterogeneous NodeSpecs to one BatchCreateSpec, or a codegen/spec-assembly bug.
Common situations: Hand-built batch specs in generic helpers that loop over mixed entities; merging specs from different mutations into one batch; misuse of sqlgraph internals outside generated code.
Related errors
- inconsistent id values for batch insert
- insert nodes to table %q: %w
- {{ base $.Config.Package }}: %w
- {{ $pkg }}: missing required field "{{ $.Name }}.{{ $f.Name
- {{ $pkg }}: missing required edge "{{ $.Name }}.{{ $e.Name }
AI-assisted analysis of ent/ent@69d5d4deb1 (2026-09-03).
Data as JSON: /api/errors/f7b6e5e3577f8050.
Report an issue: GitHub.