ent/ent · error
{{ $pkg }}: {{ $upsertOne }}.ID is not supported by MySQL dr
Error message
{{ $pkg }}: {{ $upsertOne }}.ID is not supported by MySQL driver. Use {{ $upsertOne }}.Exec instead What it means
ent's generated UpsertOne.ID(ctx) helper refuses to run on MySQL when the entity ID is a non-numeric field. MySQL lacks a RETURNING clause for INSERT ... ON DUPLICATE KEY UPDATE, so there is no way to read back the generated non-numeric (e.g. string/UUID) ID after an upsert. The library fails fast instead of returning a zero value.
Source
Thrown at entc/gen/template/dialect/sql/feature/upsert.tmpl:234
}
return u.create.Exec(ctx)
}
// ExecX is like Exec, but panics if an error occurs.
func (u *{{ $upsertOne }}) ExecX(ctx context.Context) {
if err := u.create.Exec(ctx); err != nil {
panic(err)
}
}
{{ if $.HasOneFieldID }}
// Exec executes the UPSERT query and returns the inserted/updated ID.
func (u *{{ $upsertOne }}) ID(ctx context.Context) (id {{ $.ID.Type }}, err error) {
{{- if and $udfID (not $.ID.Type.Numeric) }}
if u.create.driver.Dialect() == dialect.MySQL {
// In case of "ON CONFLICT", there is no way to get back non-numeric ID
// fields from the database since MySQL does not support the RETURNING clause.
return id, errors.New("{{ $pkg }}: {{ $upsertOne }}.ID is not supported by MySQL driver. Use {{ $upsertOne }}.Exec instead")
}
{{- end }}
node, err := u.create.Save(ctx)
if err != nil {
return id, err
}
return node.ID, nil
}
// IDX is like ID, but panics if an error occurs.
func (u *{{ $upsertOne }}) IDX(ctx context.Context) {{ $.ID.Type }} {
id, err := u.ID(ctx)
if err != nil {
panic(err)
}
return id
}
{{ end }}View on GitHub (pinned to 69d5d4deb1)
Solutions
- Use Exec(ctx) instead of ID(ctx) and set the ID yourself before the upsert (e.g. entity.SetID(uuid.New()) on the create builder), so no read-back is needed
- Pass explicit OnConflictColumns and query the row afterwards with a normal Query if you must know which row resulted
- Switch the ID field to a numeric type (int) if the schema allows it, so MySQL's LastInsertId path works
- Use a database that supports RETURNING (Postgres/SQLite) for this operation
Example fix
// before (MySQL, UUID PK)
id, err := client.User.Create().SetID(uuid.New()).SetName("a").OnConflict(sql.DoNothing()).ID(ctx)
// after
err := client.User.Create().SetID(uuid.New()).SetName("a").OnConflict(sql.DoNothing()).Exec(ctx)
// the UUID is already known: id := uuid.New() before the call Defensive patterns
Strategy: validation
Validate before calling
func mysqlNonNumericIDUpsert(dialectName string, idType reflect.Kind) bool {
return dialectName == dialect.MySQL && (idType == reflect.String)
} Type guard
func isTxDriver(d dialect.Driver) bool { _, ok := d.(*txDriver); return ok } // for tx case; here: check dialect before calling ID() Try / catch
id, err := u.ID(ctx)
if err != nil && strings.Contains(err.Error(), "not supported by MySQL") {
// fallback: ID was set client-side before the upsert
id = preGeneratedID
} Prevention
- Always generate UUIDs client-side when targeting MySQL upserts
- Prefer Exec(ctx) for MySQL upserts; reserve ID(ctx) for RETURNING-capable dialects
- Add a code-review check: non-numeric ID schemas should never call UpsertOne.ID
When it happens
Trigger: Calling u.ID(ctx) on an UpsertOne builder (created via client.<Entity>.Create().OnConflict(...).UpdateNewValues().ID(ctx)) where the entity's ID field is non-numeric (e.g. field.UUID / field.String ID) and the driver is MySQL.
Common situations: Projects that generate UUID primary keys and later add upsert logic; code written for Postgres (which supports ON CONFLICT ... RETURNING) ported to MySQL; copy-pasted upsert examples that assume numeric auto-increment IDs.
Related errors
- {{ $pkg }}: missing options for {{ $builder }}.OnConflict
- ent: cannot start a transaction within a transaction
- {{ $pkg }}: missing "{{ $.Name }}.{{ $.ID.Name }}" for updat
- {{ $pkg }}: missing "{{ $.Name }}.{{ $id.Name }}" for update
- edge schema {{ $.Name }} does not support getting old values
AI-assisted analysis of ent/ent@69d5d4deb1 (2026-09-03).
Data as JSON: /api/errors/1a0d7190197dd966.
Report an issue: GitHub.