vitessio/vitess · error
direct DDL is disabled
Error message
direct DDL is disabled
What it means
ErrDirectDDLDisabled is a sentinel error returned by TryExecute when a direct (in-place) DDL statement is attempted while direct DDL is disabled in the tablet's online-DDL configuration. It forces users to route schema changes through online DDL (gh-ost/pt-osc) or explicitly re-enable direct DDL.
Source
Thrown at go/vt/schema/online_ddl.go:51
var (
onlineDdlUUIDRegexp = regexp.MustCompile(`^[0-f]{8}_[0-f]{4}_[0-f]{4}_[0-f]{4}_[0-f]{12}$`)
onlineDDLGeneratedTableNameRegexp = regexp.MustCompile(`^_[0-f]{8}_[0-f]{4}_[0-f]{4}_[0-f]{4}_[0-f]{12}_([0-9]{14})_(gho|ghc|del|new|vrepl)$`)
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
}View on GitHub (pinned to 01a25a7d17)
Solutions
- Use an online DDL strategy: prefix the statement with a comment like /*vt+ ddl_strategy=gh-ost */ or set -ddl_strategy appropriately
- Change the tablet's ddl_strategy flag to allow direct DDL (e.g. 'direct') if acceptable for the operation
- Route the change through vtctldclient ApplySchema for coordinated application
- Check errors.Is(err, schema.ErrDirectDDLDisabled) in tooling to present a friendly message
Example fix
-- before ALTER TABLE t ADD COLUMN c INT; -- after ALTER /*vt+ ddl_strategy=gh-ost */ TABLE t ADD COLUMN c INT;
Defensive patterns
Strategy: validation
Validate before calling
strategy := viper.GetString("ddl_strategy")
if !strings.Contains(strategy, "direct") && isDirectDDL(stmt) {
return fmt.Errorf("rewrite %s with ddl_strategy=online or enable direct DDL", stmt)
} Try / catch
_, err := tm.TryExecute(ctx, stmt)
if errors.Is(err, schema.ErrDirectDDLDisabled) {
// fall back to online DDL strategy or surface config guidance
} Prevention
- Embed an explicit /*vt+ ddl_strategy=... */ comment in all migrations
- Keep a keyspace-level policy for which strategies are permitted
- Test migrations in CI with the same ddl_strategy as production
- Educate tooling authors that direct DDL may be disabled by policy
When it happens
Trigger: Submitting a plain DDL statement (ALTER/CREATE/DROP without an online-DDL prefix) to the vttablet ExecuteApiPath / query service with -ddl_strategy set such that direct DDL is disallowed (e.g. ddl_strategy='online' or empty direct allowance).
Common situations: Environments where operators disabled direct DDL to protect production (long ALTERs locking tables); CI clusters configured for online-DDL only; users unaware that ddl_strategy governs direct vs online execution.
Related errors
- online DDL is disabled
- Foreign key found
- RENAME clause found
- partial row image encountered: ensure binlog_row_image is se
- %s not found in any of %s/{%s}
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/e6186ef5c66fd042.
Report an issue: GitHub.