{"record":{"id":"1a0d7190197dd966","repo":"ent/ent","slug":"pkg-upsertone-id-is-not-supported-b","errorCode":null,"errorMessage":"{{ $pkg }}: {{ $upsertOne }}.ID is not supported by MySQL driver. Use {{ $upsertOne }}.Exec instead","messagePattern":"(.+?)\\}: (.+?)\\}\\.ID is not supported by MySQL driver\\. Use (.+?)\\}\\.Exec instead","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"entc/gen/template/dialect/sql/feature/upsert.tmpl","lineNumber":234,"sourceCode":"\t}\n\treturn u.create.Exec(ctx)\n}\n\n// ExecX is like Exec, but panics if an error occurs.\nfunc (u *{{ $upsertOne }}) ExecX(ctx context.Context) {\n\tif err := u.create.Exec(ctx); err != nil {\n\t\tpanic(err)\n\t}\n}\n\n{{ if $.HasOneFieldID }}\n\t// Exec executes the UPSERT query and returns the inserted/updated ID.\n\tfunc (u *{{ $upsertOne }}) ID(ctx context.Context) (id {{ $.ID.Type }}, err error) {\n\t\t{{- if and $udfID (not $.ID.Type.Numeric) }}\n\t\t\tif u.create.driver.Dialect() == dialect.MySQL {\n\t\t\t\t// In case of \"ON CONFLICT\", there is no way to get back non-numeric ID\n\t\t\t\t// fields from the database since MySQL does not support the RETURNING clause.\n\t\t\t\treturn id, errors.New(\"{{ $pkg }}: {{ $upsertOne }}.ID is not supported by MySQL driver. Use {{ $upsertOne }}.Exec instead\")\n\t\t\t}\n\t\t{{- end }}\n\t\tnode, err := u.create.Save(ctx)\n\t\tif err != nil {\n\t\t\treturn id, err\n\t\t}\n\t\treturn node.ID, nil\n\t}\n\n\t// IDX is like ID, but panics if an error occurs.\n\tfunc (u *{{ $upsertOne }}) IDX(ctx context.Context) {{ $.ID.Type }} {\n\t\tid, err := u.ID(ctx)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\treturn id\n\t}\n{{ end }}","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/ent/ent/blob/69d5d4deb19599f129166634e09d33addcf3f2cc/entc/gen/template/dialect/sql/feature/upsert.tmpl#L216-L252","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (MySQL, UUID PK)\nid, err := client.User.Create().SetID(uuid.New()).SetName(\"a\").OnConflict(sql.DoNothing()).ID(ctx)\n// after\nerr := client.User.Create().SetID(uuid.New()).SetName(\"a\").OnConflict(sql.DoNothing()).Exec(ctx)\n// the UUID is already known: id := uuid.New() before the call","handlingStrategy":"validation","validationCode":"func mysqlNonNumericIDUpsert(dialectName string, idType reflect.Kind) bool {\n    return dialectName == dialect.MySQL && (idType == reflect.String)\n}","typeGuard":"func isTxDriver(d dialect.Driver) bool { _, ok := d.(*txDriver); return ok } // for tx case; here: check dialect before calling ID()","tryCatchPattern":"id, err := u.ID(ctx)\nif err != nil && strings.Contains(err.Error(), \"not supported by MySQL\") {\n    // fallback: ID was set client-side before the upsert\n    id = preGeneratedID\n}","preventionTips":["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"],"tags":["ent","mysql","upsert","go-codegen"],"backgroundTag":"unsupported-driver-feature","analyzedSha":"69d5d4deb19599f129166634e09d33addcf3f2cc","analyzedAt":"2026-09-03T16:51:39.799Z","contentChangedAt":"2026-09-03T16:51:39.799Z","schemaVersion":2},"datasetVersion":"2026-09-11T00:17:11.886Z"}