vitessio/vitess · error

AfterSchema differs

Error message

AfterSchema differs

What it means

After executing the DDL, ApplySchemaChange re-reads the schema and computes diffs against the caller-supplied expected AfterSchema. Mismatches are logged; unless change.Force is set, the call returns 'AfterSchema differs', signaling the DDL's result did not match expectations.

Source

Thrown at go/vt/mysqlctl/schema.go:573

	}

	// get AfterSchema
	afterSchema, err := mysqld.GetSchema(ctx, dbName, req)
	if err != nil {
		return nil, err
	}

	// compare to the provided AfterSchema
	if change.AfterSchema != nil {
		schemaDiffs := tmutils.DiffSchemaToArray("actual", afterSchema, "expected", change.AfterSchema)
		if len(schemaDiffs) > 0 {
			for _, msg := range schemaDiffs {
				log.Warn(fmt.Sprintf("AfterSchema differs: %v", msg))
			}
			if change.Force {
				log.Warn("AfterSchema differs, not reporting error")
			} else {
				return nil, errors.New("AfterSchema differs")
			}
		}
	}

	return &tabletmanagerdatapb.SchemaChangeResult{BeforeSchema: beforeSchema, AfterSchema: afterSchema}, nil
}

// GetPrimaryKeyEquivalentColumns can be used if the table has
// no defined PRIMARY KEY. It will return the columns in a
// viable PRIMARY KEY equivalent (PKE) -- a NON-NULL UNIQUE
// KEY -- along with that index's name in the specified table.
// When multiple PKE indexes are available it will attempt to
// choose the most efficient one based on the column data types
// and the number of columns in the index. See here for the data
// type storage sizes:
//
//	https://dev.mysql.com/doc/refman/en/storage-requirements.html
//

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Wait for the online-DDL migration to complete (ShowMigrationStatus / wait until complete) before asserting AfterSchema
  2. Generate AfterSchema programmatically (apply the diff to BeforeSchema) rather than hand-writing it
  3. Inspect logged schemaDiffs messages and correct the expected AfterSchema accordingly
  4. Use Force=true only when the observed after-state is verified acceptable

Example fix

// before
res, err := tm.ApplySchemaChange(ctx, &tms.SchemaChange{SQL: sql, AfterSchema: handWritten})
// after
status, _ := tm.ShowMigrationStatus(ctx, uuid) // wait for online DDL completion
res, err := tm.ApplySchemaChange(ctx, &tms.SchemaChange{SQL: sql, AfterSchema: computedFromBeforePlusDiff})
Defensive patterns

Strategy: try-catch

Validate before calling

// wait for online DDL completion before verifying after-schema
status, err := tm.ShowMigrationStatus(ctx, uuid)
if err != nil || !status.IsComplete() {
    return fmt.Errorf("migration not complete; cannot assert AfterSchema yet")
}

Try / catch

res, err := tm.ApplySchemaChange(ctx, change)
if err != nil && strings.Contains(err.Error(), "AfterSchema differs") {
    log.Warn("post-DDL schema differs from expectation; inspect logged schemaDiffs")
    // re-fetch and compare, or set Force only if diff reviewed
}

Prevention

When it happens

Trigger: Calling ApplySchemaChange with AfterSchema set in the SchemaChange, and the post-DDL schema differs — e.g. online DDL migration still in progress, triggers/character-set differences not captured, DDL executed but with different effects than predicted.

Common situations: CHANGE/REVERT workflows where the expected after-state was hand-written; online DDL (gh-ost/pt-osc) still cutover-ing so the visible schema hasn't changed yet; MySQL version differences producing extra/different schema attributes.

Related errors


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