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 visited

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Remove the FOREIGN KEY clause and manage constraints outside online DDL (apply that specific statement with ddl_strategy=direct if allowed)
  2. Drop the FK separately with a direct DDL statement, then run the main ALTER online
  3. If FKs are essential to the migration, avoid online-DDL strategy for this statement
  4. 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

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


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/728780614abb721c. Report an issue: GitHub.