vitessio/vitess · error · ErrWorkflowDeleteWritesSwitched

cannot delete workflow because you have already switched wri

Error message

cannot delete workflow because you have already switched write traffic

What it means

WorkflowDelete (and TestWorkflowDelete) refuses to delete a MoveTables workflow after write traffic has already been switched to the target, because deleting it would drop the source tables' replication safety net / reverse replication. ErrWorkflowDeleteWritesSwitched is the sentinel for this guarded state.

Source

Thrown at go/vt/vtctl/workflow/server.go:128

	lockTablesCycleDelay = time.Duration(100 * time.Millisecond)

	SqlUnfreezeWorkflow = "update _vt.vreplication set state='Running', message='' where db_name=%a and workflow=%a"
)

var (
	// ErrInvalidWorkflow is a catchall error type for conditions that should be
	// impossible when operating on a workflow.
	ErrInvalidWorkflow = errors.New("invalid workflow")
	// ErrMultipleSourceKeyspaces occurs when a workflow somehow has multiple
	// source keyspaces across different shard primaries. This should be
	// impossible.
	ErrMultipleSourceKeyspaces = errors.New("multiple source keyspaces for a single workflow")
	// ErrMultipleTargetKeyspaces occurs when a workflow somehow has multiple
	// target keyspaces across different shard primaries. This should be
	// impossible.
	ErrMultipleTargetKeyspaces          = errors.New("multiple target keyspaces for a single workflow")
	ErrWorkflowCompleteNotFullySwitched = errors.New("cannot complete workflow because you have not yet switched all read and write traffic")
	ErrWorkflowDeleteWritesSwitched     = errors.New("cannot delete workflow because you have already switched write traffic")
)

// Server provides an API to work with Vitess workflows, like vreplication
// workflows (MoveTables, Reshard, etc) and schema migration workflows.
type Server struct {
	ts  *topo.Server
	tmc tmclient.TabletManagerClient
	// Limit the number of concurrent background goroutines if needed.
	sem     *semaphore.Weighted
	env     *vtenv.Environment
	options serverOptions
}

// NewServer returns a new server instance with the given topo.Server and
// TabletManagerClient.
func NewServer(env *vtenv.Environment, ts *topo.Server, tmc tmclient.TabletManagerClient, opts ...ServerOption) *Server {
	s := &Server{
		ts:  ts,

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Use `MoveTablesComplete` (or `WorkflowDelete` with the appropriate drop-source/table-removal options) instead of a plain delete after writes are switched.
  2. If you truly must remove the workflow, explicitly pass the drop-source / table removal type that documents the intent.
  3. Roll back write traffic (reverse SwitchWrites) first if the deletion was not intended.

Example fix

// before
vtctldclient WorkflowDelete --workflow sales commerce // error: writes already switched
// after
vtctldclient MoveTablesComplete --workflow sales --drop_source commerce
Defensive patterns

Strategy: validation

Validate before calling

if writesSwitched(keyspace, workflow) {
    return fmt.Errorf("use MoveTablesComplete --drop_source instead of delete for %s", workflow)
}

Type guard

func isDeleteAfterWritesErr(err error) bool {
    return errors.Is(err, workflow.ErrWorkflowDeleteWritesSwitched)
}

Try / catch

if err := client.WorkflowDelete(ctx, ks, wf, false); err != nil {
    if errors.Is(err, workflow.ErrWorkflowDeleteWritesSwitched) {
        return client.MoveTablesComplete(ctx, ks, wf) // intended cleanup path
    }
    return err
}

Prevention

When it happens

Trigger: Calling WorkflowDelete on a workflow where SwitchWrites has completed (writes are now on the target keyspace) and the source tables would be removed or replication torn down unsafely.

Common situations: Operators cleaning up a finished MoveTables migration who call the plain delete command instead of `MoveTablesComplete`/DropSource with the intended table-removal behavior.

Related errors


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