vitessio/vitess · error

Last_SQL_Error: ${LastSQL_Error}, Last_IO_Error: ${LastIO_Er

Error message

Last_SQL_Error: ${LastSQL_Error}, Last_IO_Error: ${LastIO_Error}

What it means

WaitForReplicationStart polls SHOW SLAVE STATUS after issuing START SLAVE and aggregates any replication thread failures into a single error. When Last_SQL_Error (replica SQL thread) or Last_IO_Error (replica IO thread) is non-empty, they are joined with ", " and returned as one error. It reports why replication did not come up cleanly.

Source

Thrown at go/vt/mysqlctl/replication.go:157

		if err != nil {
			return err
		}

		if replicaStatus.Running() {
			return nil
		}
		time.Sleep(time.Second)
	}
	errs := make([]string, 0, 2)
	if replicaStatus.LastSQLError != "" {
		errs = append(errs, "Last_SQL_Error: "+replicaStatus.LastSQLError)
	}
	if replicaStatus.LastIOError != "" {
		errs = append(errs, "Last_IO_Error: "+replicaStatus.LastIOError)
	}

	if len(errs) != 0 {
		return errors.New(strings.Join(errs, ", "))
	}
	return nil
}

// StartReplication starts replication.
func (mysqld *Mysqld) StartReplication(ctx context.Context, hookExtraEnv map[string]string) error {
	conn, err := getPoolReconnect(ctx, mysqld.dbaPool)
	if err != nil {
		return err
	}
	defer conn.Recycle()

	if err := mysqld.executeSuperQueryListConn(ctx, conn, []string{conn.Conn.StartReplicationCommand()}); err != nil {
		return err
	}

	h := hook.NewSimpleHook("postflight_start_slave")
	h.ExtraEnv = hookExtraEnv

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Read the embedded Last_SQL_Error / Last_IO_Error text in the message — it names the underlying replication failure.
  2. For IO errors, verify replication credentials, master host/port, and network/TLS connectivity from the replica (CHECK REPLICA STATUS / SHOW REPLICA STATUS).
  3. For SQL errors, inspect the offending binlog event; skip with mysqlctl's error handling or re-provision the replica from a fresh backup.
  4. Re-run WaitForReplicationStart after fixing; ensure the replica threads actually restart (START REPLICA).

Example fix

// before
if err := WaitForReplicationStart(ctx, mysqld, 30*time.Second); err != nil {
    log.Errorf("replication failed: %v", err)
}

// after
if err := WaitForReplicationStart(ctx, mysqld, 30*time.Second); err != nil {
    if strings.Contains(err.Error(), "Last_IO_Error:") {
        log.Warn("IO thread error, retrying after connectivity check", slog.Any("error", err))
        // verify master reachability / credentials, then retry
    } else {
        return vterrors.Wrapf(err, "replication failed to start")
    }
}
Defensive patterns

Strategy: try-catch

Try / catch

err := mysqlctl.WaitForReplicationStart(ctx, mysqld, timeout)
if err != nil {
    var ioErr, sqlErr bool
    if strings.Contains(err.Error(), "Last_IO_Error:") {
        ioErr = true
    }
    if strings.Contains(err.Error(), "Last_SQL_Error:") {
        sqlErr = true
    }
    switch {
    case ioErr:
        // connectivity/credentials: bounded retry
    case sqlErr:
        // event-level failure: needs manual intervention
    default:
        return vterrors.Wrapf(err, "replication start")
    }
}

Prevention

When it happens

Trigger: Calling WaitForReplicationStart (directly or via StartReplication paths, or during executeFullBackup) where replicaStatus.LastSQL_Error or replicaStatus.LastIO_Error is non-empty after the wait window; the strings are concatenated as "Last_SQL_Error: <val>, Last_IO_Error: <val>" and returned.

Common situations: Replica IO thread can't connect to the master (wrong credentials/host, network partition, TLS mismatch); SQL thread halted by a duplicate-key or corrupt relay-log error; starting replication against a misprovisioned replica after restore.

Related errors


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