{"record":{"id":"df91f705bdcc098e","repo":"vitessio/vitess","slug":"vt09016-df91f7","errorCode":"VT09016","errorMessage":"Cannot delete or update a parent row: a foreign key constraint fails","messagePattern":"Cannot delete or update a parent row: a foreign key constraint fails","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtgate/planbuilder/operators/update.go","lineNumber":650,"sourceCode":"\tvar childWhereExpr sqlparser.Expr = compExpr\n\n\t// In the case of non-literal updates, we need to assign bindvariables for storing the updated value of the columns\n\t// coming from the SELECT query.\n\tif len(nonLiteralUpdateInfo) > 0 {\n\t\tfor idx, info := range nonLiteralUpdateInfo {\n\t\t\tinfo.UpdateExprBvName = ctx.ReservedVars.ReserveVariable(foreignKeyUpdateExpr)\n\t\t\tnonLiteralUpdateInfo[idx] = info\n\t\t}\n\t}\n\n\tvar childOp Operator\n\tswitch fk.OnUpdate {\n\tcase sqlparser.Cascade:\n\t\tchildOp = buildChildUpdOpForCascade(ctx, fk, childWhereExpr, nonLiteralUpdateInfo, updatedTable)\n\tcase sqlparser.SetNull:\n\t\tchildOp = buildChildUpdOpForSetNull(ctx, fk, childWhereExpr, nonLiteralUpdateInfo, updatedTable)\n\tcase sqlparser.SetDefault:\n\t\tpanic(vterrors.VT09016())\n\t}\n\n\treturn &FkChild{\n\t\tBVName:         bvName,\n\t\tCols:           selectOffsets,\n\t\tOp:             childOp,\n\t\tNonLiteralInfo: nonLiteralUpdateInfo,\n\t}\n}\n\n// buildChildUpdOpForCascade builds the child update statement operator for the CASCADE type foreign key constraint.\n// The query looks like this -\n//\n//\t`UPDATE <child_table> SET <child_column_updated_using_update_exprs_from_parent_update_query> WHERE <child_columns_in_fk> IN (<bind variable for the output from SELECT>)`\nfunc buildChildUpdOpForCascade(ctx *plancontext.PlanningContext, fk vindexes.ChildFKInfo, childWhereExpr sqlparser.Expr, nonLiteralUpdateInfo []engine.NonLiteralUpdateInfo, updatedTable *vindexes.BaseTable) Operator {\n\t// The update expressions are the same as the update expressions in the parent update query\n\t// with the column names replaced with the child column names.\n\tvar childUpdateExprs sqlparser.UpdateExprs","sourceCodeStart":632,"sourceCodeEnd":668,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/planbuilder/operators/update.go#L632-L668","documentation":"VT09016: the planner cannot build an UPDATE cascade operation for a foreign key declared ON UPDATE SET DEFAULT. Vitess does not implement propagating SET DEFAULT updates from parent to child rows, so it fails the statement with MySQL's parent-row FK message semantics.","triggerScenarios":"Updating a parent table's referenced column where the child FK is defined with ON UPDATE SET DEFAULT, reached via createFkChildForUpdate's switch on fk.OnUpdate.","commonSituations":"Schemas written for MySQL using ON UPDATE SET DEFAULT that are then run behind Vitess with managed foreign keys; legacy schemas where SET DEFAULT was common.","solutions":["Change the child FK to ON UPDATE CASCADE, SET NULL, or NO ACTION.","If the default behavior is required, handle the update in application logic across parent and child tables.","Set foreign_key_mode to unmanaged so MySQL (not Vitess) enforces FKs, if compatible.","Rework the schema to avoid SET DEFAULT on update."],"exampleFix":"-- before\nFOREIGN KEY (pid) REFERENCES parent(id) ON UPDATE SET DEFAULT;\n-- after\nFOREIGN KEY (pid) REFERENCES parent(id) ON UPDATE CASCADE;","handlingStrategy":"validation","validationCode":"// Reject schemas using ON UPDATE SET DEFAULT under managed FKs\nfor _, fk := range allForeignKeys(schema) {\n  if fk.OnUpdate == \"SET DEFAULT\" {\n    return fmt.Errorf(\"FK %s uses unsupported ON UPDATE SET DEFAULT\", fk.Name)\n  }\n}","typeGuard":null,"tryCatchPattern":"res, err := vtgate.Execute(ctx, session, query, vars)\nif err != nil && strings.Contains(err.Error(), \"VT09016\") {\n  return applySchemaFix() // migrate FK to CASCADE/SET NULL and retry\n}","preventionTips":["Audit DDL for ON UPDATE SET DEFAULT before enabling managed FK mode","Use CASCADE or SET NULL instead of SET DEFAULT","Keep FK behavior within Vitess's supported set (CASCADE, SET NULL, NO ACTION)"],"tags":["vitess","vt09016","foreign-key","set-default","unsupported"],"backgroundTag":"fk-set-default-unsupported","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}