vitessio/vitess · error

routing still exists from keyspace %s table %s to %s

Error message

routing still exists from keyspace %s table %s to %s

What it means

As a final completion check, the workflow validator reads the current RoutingRules and verifies that no routing rule still points a `fromTable` at a source-keyspace table participating in the workflow. If a rule maps traffic into the source keyspace's table (e.g. customer.t still routed via a MoveTables rule), the migration is not fully cut over and this error is recorded.

Source

Thrown at go/vt/vtctl/workflow/utils.go:596

				}
			}
			return nil
		})
	}
	wg.Wait()

	if !ts.keepRoutingRules {
		// Check if table is routable.
		if ts.MigrationType() == binlogdatapb.MigrationType_TABLES {
			rules, err := topotools.GetRoutingRules(ctx, ts.TopoServer())
			if err != nil {
				rec.RecordError(errors.New("could not get RoutingRules"))
			}
			for fromTable, toTables := range rules {
				for _, toTable := range toTables {
					for _, table := range ts.Tables() {
						if toTable == fmt.Sprintf("%s.%s", ts.SourceKeyspaceName(), table) {
							rec.RecordError(fmt.Errorf("routing still exists from keyspace %s table %s to %s", ts.SourceKeyspaceName(), table, fromTable))
						}
					}
				}
			}
		}
	}
	if rec.HasErrors() {
		return fmt.Errorf("%s", strings.Join(rec.ErrorStrings(), "\n"))
	}
	return nil
}

// ReverseWorkflowName returns the "reversed" name of a workflow. For a
// "forward" workflow, this is the workflow name with "_reverse" appended, and
// for a "reversed" workflow, this is the workflow name with the "_reverse"
// suffix removed.
func ReverseWorkflowName(workflow string) string {
	if strings.HasSuffix(workflow, reverseSuffix) {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Run the workflow's traffic-switch/finalize step that removes the routing rules (SwitchTraffic with all tables, or RemoveRoutingRule for the stale entries).
  2. Inspect `vtctldclient GetRoutingRules` and delete rules whose to-tables point at sourceKeyspace.<table>.
  3. Re-run validation after cleaning the rules; it should find no routing into the source keyspace.

Example fix

// before (routing rules still contain)
//   from: customer.t  ->  [ks1.t]
// error: routing still exists from keyspace ks1 table t to customer.t
// after
vtctldclient RemoveRoutingRule --table=customer.t
vtctldclient ApplyRoutingRules --rules-file=cleaned.json
Defensive patterns

Strategy: validation

Validate before calling

// ensure no routing rule targets the source keyspace tables
rules := getRoutingRules()
for from, tos := range rules {
    for _, to := range tos {
        if strings.HasPrefix(to, sourceKeyspace+".") {
            return fmt.Errorf("stale routing rule %s -> %s", from, to)
        }
    }
}

Prevention

When it happens

Trigger: Running validateWorkflowHasCompleted (via SwitchTraffic/final validation) while RoutingRules still contain entries mapping a from-table to `sourceKeyspace.table` for a table in the workflow's table set — i.e. the RemoveRoutingRule step never ran or added rules back.

Common situations: Finalizing MoveTables before switching routing rules; a second workflow re-adding routing to the source keyspace; partial traffic switch leaving some table rules in place.

Related errors


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