{"record":{"id":"22fcba5649d8d070","repo":"vitessio/vitess","slug":"cannot-complete-workflow-because-you-have-not-yet","errorCode":null,"errorMessage":"cannot complete workflow because you have not yet switched all read and write traffic","messagePattern":"cannot complete workflow because you have not yet switched all read and write traffic","errorType":"error_code","errorClass":"ErrWorkflowCompleteNotFullySwitched","httpStatus":null,"severity":"error","filePath":"go/vt/vtctl/workflow/server.go","lineNumber":127,"sourceCode":"\t// Time to wait between LOCK TABLES cycles on the sources during SwitchWrites.\n\tlockTablesCycleDelay = time.Duration(100 * time.Millisecond)\n\n\tSqlUnfreezeWorkflow = \"update _vt.vreplication set state='Running', message='' where db_name=%a and workflow=%a\"\n)\n\nvar (\n\t// ErrInvalidWorkflow is a catchall error type for conditions that should be\n\t// impossible when operating on a workflow.\n\tErrInvalidWorkflow = errors.New(\"invalid workflow\")\n\t// ErrMultipleSourceKeyspaces occurs when a workflow somehow has multiple\n\t// source keyspaces across different shard primaries. This should be\n\t// impossible.\n\tErrMultipleSourceKeyspaces = errors.New(\"multiple source keyspaces for a single workflow\")\n\t// ErrMultipleTargetKeyspaces occurs when a workflow somehow has multiple\n\t// target keyspaces across different shard primaries. This should be\n\t// impossible.\n\tErrMultipleTargetKeyspaces          = errors.New(\"multiple target keyspaces for a single workflow\")\n\tErrWorkflowCompleteNotFullySwitched = errors.New(\"cannot complete workflow because you have not yet switched all read and write traffic\")\n\tErrWorkflowDeleteWritesSwitched     = errors.New(\"cannot delete workflow because you have already switched write traffic\")\n)\n\n// Server provides an API to work with Vitess workflows, like vreplication\n// workflows (MoveTables, Reshard, etc) and schema migration workflows.\ntype Server struct {\n\tts  *topo.Server\n\ttmc tmclient.TabletManagerClient\n\t// Limit the number of concurrent background goroutines if needed.\n\tsem     *semaphore.Weighted\n\tenv     *vtenv.Environment\n\toptions serverOptions\n}\n\n// NewServer returns a new server instance with the given topo.Server and\n// TabletManagerClient.\nfunc NewServer(env *vtenv.Environment, ts *topo.Server, tmc tmclient.TabletManagerClient, opts ...ServerOption) *Server {\n\ts := &Server{","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtctl/workflow/server.go#L109-L145","documentation":"MoveTablesComplete refuses to finalize a MoveTables workflow when read and write traffic has not yet been fully switched to the target keyspace. Completing early would drop replication while the source still serves traffic, losing writes. The sentinel ErrWorkflowCompleteNotFullySwitched makes this state explicit to callers.","triggerScenarios":"Calling MoveTablesComplete (vtctldserver Workflow complete) before `SwitchReads` and `SwitchWrites`/`MigrateTraffic` have moved all traffic for every table in the workflow.","commonSituations":"Operators running the final cutover step too early in a MoveTables migration, or some tables' read traffic still routed to the source after a partial switch.","solutions":["Run `SwitchReads -tablet_type=rdonly,replica` and then `SwitchReads -tablet_type=primary` for the workflow.","Run `SwitchWrites` (or MigrateTraffic) to completion, confirming all writes are served by the target.","Re-run MoveTablesComplete only after `Workflow Show` confirms all traffic is switched."],"exampleFix":"// before\nvtctldclient MoveTablesComplete --workflow sales commerce\n// error: not yet switched all traffic\n// after\nvtctldclient SwitchReads --tablet_type=replica commerce.sales\nvtctldclient SwitchWrites --shard 0 commerce.sales\nvtctldclient MoveTablesComplete --workflow sales commerce","handlingStrategy":"validation","validationCode":"state, _ := client.GetWorkflowState(ctx, targetKeyspace, workflow)\nif !state.AllTrafficSwitched() {\n    return fmt.Errorf(\"switch reads and writes before completing %s\", workflow)\n}","typeGuard":null,"tryCatchPattern":"if err := client.MoveTablesComplete(ctx, ks, wf); err != nil {\n    if errors.Is(err, workflow.ErrWorkflowCompleteNotFullySwitched) {\n        return fmt.Errorf(\"run SwitchReads + SwitchWrites for %s first: %w\", wf, err)\n    }\n    return err\n}","preventionTips":["Always run SwitchReads (rdonly, replica, primary) then SwitchWrites before complete","Confirm with Workflow Show that all traffic is on the target","Script the cutover as an ordered pipeline rather than manual steps"],"tags":["vitess","movetables","traffic-switch","cutover"],"backgroundTag":"workflow-traffic-not-switched","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}