vitessio/vitess · error
Foreign key found
Error message
Foreign key found
What it means
ErrForeignKeyFound is a sentinel error indicating a FOREIGN KEY clause was found in a DDL statement during online-DDL validation (validateWalk / onlineDDLStatementSanity). Online migration tools (gh-ost/pt-osc) cannot safely handle foreign keys, so statements containing them are rejected up front.
Source
Thrown at go/vt/schema/online_ddl.go:55
ptOSCGeneratedTableNameRegexp = regexp.MustCompile(`^_.*_old$`)
migrationContextValidatorRegexp = regexp.MustCompile(`^[\w:-]*$`)
)
var onlineDDLInternalTableHintsMap = map[string]bool{
"vrp": true, // vreplication
"gho": true, // gh-ost
"ghc": true, // gh-ost
"del": true, // gh-ost
"new": true, // pt-osc
}
var (
// ErrDirectDDLDisabled is returned when direct DDL is disabled, and a user attempts to run a DDL statement
ErrDirectDDLDisabled = errors.New("direct DDL is disabled")
// ErrOnlineDDLDisabled is returned when online DDL is disabled, and a user attempts to run an online DDL operation (submit, review, control)
ErrOnlineDDLDisabled = errors.New("online DDL is disabled")
// ErrForeignKeyFound indicates any finding of FOREIGN KEY clause in a DDL statement
ErrForeignKeyFound = errors.New("Foreign key found")
// ErrRenameTableFound indicates finding of ALTER TABLE...RENAME in ddl statement
ErrRenameTableFound = errors.New("RENAME clause found")
)
const (
SchemaMigrationsTableName = "schema_migrations"
RevertActionStr = "revert"
)
// ValidateMigrationContext validates that the given migration context only uses valid characters
func ValidateMigrationContext(migrationContext string) error {
if migrationContextValidatorRegexp.MatchString(migrationContext) {
return nil
}
return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid characters in migration_context %v. Use alphanumeric, dash, underscore and colon only", migrationContext)
}
// when validateWalk returns true, then the child nodes are also visitedView on GitHub (pinned to 01a25a7d17)
Solutions
- Remove the FOREIGN KEY clause and manage constraints outside online DDL (apply that specific statement with ddl_strategy=direct if allowed)
- Drop the FK separately with a direct DDL statement, then run the main ALTER online
- If FKs are essential to the migration, avoid online-DDL strategy for this statement
- Detect errors.Is(err, schema.ErrForeignKeyFound) in tooling to split the migration
Example fix
-- before ALTER /*vt+ ddl_strategy=gh-ost */ TABLE t ADD CONSTRAINT fk FOREIGN KEY (a) REFERENCES p(id); // after: split statements ALTER /*vt+ ddl_strategy=direct */ TABLE t ADD CONSTRAINT fk FOREIGN KEY (a) REFERENCES p(id); ALTER /*vt+ ddl_strategy=gh-ost */ TABLE t ADD COLUMN c INT;
Defensive patterns
Strategy: validation
Validate before calling
ast, _ := sqlparser.Parse(stmt)
found := false
sqlparser.Walk(func(node sqlparser.SQLNode) (bool) { return true }, ast) // use validateWalk equivalent
if containsForeignKeyClause(ast) {
return fmt.Errorf("split FK change out of online DDL statement")
} Try / catch
_, err := tm.TryExecute(ctx, stmt)
if errors.Is(err, schema.ErrForeignKeyFound) {
return fmt.Errorf("rewrite %s without FOREIGN KEY or use direct DDL for it", stmt)
} Prevention
- Audit schemas for FKs before enabling online DDL workflows
- Generate migrations that separate constraint changes from column/table changes
- Prefer dropping FKs if the app enforces integrity, simplifying online DDL
- Lint migration files for FOREIGN KEY clauses targeting online strategy
When it happens
Trigger: Submitting an ALTER TABLE ... ADD/DROP FOREIGN KEY (or CREATE TABLE with FK) through the online-DDL path; the AST walk encounters a ForeignKey definition and returns ErrForeignKeyFound.
Common situations: Schemas relying on referential integrity being changed via gh-ost; users unaware of the online-DDL foreign-key limitation; generated migrations (ORM autogen) including FK constraints.
Related errors
- direct DDL is disabled
- online DDL is disabled
- RENAME clause found
- schemas differ on table %v: %s: %v differs from: %s: %v
- could not set the permissive sql_mode on target using %s: %v
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/728780614abb721c.
Report an issue: GitHub.