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

  1. Group nodes by table and issue one sqlgraph batch insert per table.
  2. If using generated code, ensure each entity type's CreateBulk is used on its own client (client.Foo.CreateBulk vs client.Bar.CreateBulk).
  3. 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

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


AI-assisted analysis of ent/ent@69d5d4deb1 (2026-09-03). Data as JSON: /api/errors/f7b6e5e3577f8050. Report an issue: GitHub.