vitessio/vitess · error

cannot run migration %s reverting %s: found %d artifact tabl

Error message

cannot run migration %s reverting %s: found %d artifact tables, expected maximum 1

What it means

When reverting a CREATE TABLE migration, vttablet inspects the artifacts (renamed/sentry tables) left behind by the original CREATE. A valid CREATE TABLE revert can have at most 1 artifact table (the sentry for a real create, none for a no-op). More than one artifact means the migration state is inconsistent and the revert is aborted.

Source

Thrown at go/vt/vttablet/onlineddl/executor.go:2214

		return err
	}

	revertedActionStr := row["ddl_action"].ToString()
	switch revertedActionStr {
	case sqlparser.CreateStr:
		{
			// We are reverting a CREATE migration. The revert is to DROP, only we don't actually
			// drop the table, we rename it into lifecycle
			// Possibly this was a CREATE TABLE IF NOT EXISTS, and possibly the table already existed
			// before the DDL, in which case the CREATE was a noop. In that scenario we _do not_ drop
			// the table.
			// We can tell the difference by looking at the artifacts. A successful CREATE TABLE, where
			// a table actually gets created, has a sentry, dummy artifact. A noop has not.

			artifacts := row["artifacts"].ToString()
			artifactTables := textutil.SplitDelimitedList(artifacts)
			if len(artifactTables) > 1 {
				return fmt.Errorf("cannot run migration %s reverting %s: found %d artifact tables, expected maximum 1", onlineDDL.UUID, revertMigration.UUID, len(artifactTables))
			}
			if len(artifactTables) == 0 {
				// This indicates no table was actually created. this must have been a CREATE TABLE IF NOT EXISTS where the table already existed.
				_ = e.onSchemaMigrationStatus(ctx, onlineDDL.UUID, schema.OnlineDDLStatusComplete, false, progressPctFull, etaSecondsNow, rowsCopiedUnknown, emptyHint)
			}

			for _, artifactTable := range artifactTables {
				if err := e.updateArtifacts(ctx, onlineDDL.UUID, artifactTable); err != nil {
					return err
				}
				onlineDDL.SQL = sqlparser.BuildParsedQuery(sqlRenameTable, revertMigration.Table, artifactTable).Query
				if _, err := e.executeDirectly(ctx, onlineDDL); err != nil {
					return err
				}
			}
		}
	case sqlparser.DropStr:
		{

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Inspect the artifacts column for the original migration and remove stale/foreign artifact table entries (after verifying with SHOW TABLES)
  2. Drop leftover artifact tables manually and clear the artifacts field, then retry the revert
  3. Re-run the original migration instead of reverting if the state cannot be repaired
  4. Verify the revert targets the correct migration UUID
Defensive patterns

Strategy: validation

Validate before calling

// Verify artifacts state before reverting a CREATE migration
row := queryRow(t, "SELECT artifacts FROM _vt.schema_migrations WHERE uuid=?", uuid)
artifacts := splitDelimited(row.artifacts)
if len(artifacts) > 1 {
    return fmt.Errorf("migration %s has %d artifacts; clean up before revert", uuid, len(artifacts))
}

Try / catch

err := submitRevert(ctx, uuid)
if err != nil && strings.Contains(err.Error(), "artifact tables, expected maximum 1") {
    // inspect and clean artifact tables manually, then retry
}

Prevention

When it happens

Trigger: Executing a REVERT of a CREATE TABLE migration whose 'artifacts' column in _vt.schema_migrations contains more than one delimited table name.

Common situations: Artifacts from a different migration were accidentally merged into the row, retry/replay of an old migration produced duplicate artifacts, or manual edits to the schema_migrations table.

Related errors


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