vitessio/vitess · error

could not get RoutingRules

Error message

could not get RoutingRules

What it means

During workflow-completion validation, when --keep-routing-rules is not set, vtctl reads the global RoutingRules from the topology to verify no routing entries still point at the source keyspace. If reading RoutingRules fails, this error is recorded. Note: rules is then still used in the loop below, which is why a nil/failed read must be treated as fatal.

Source

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

				return nil
			}
			for _, stream := range res.Streams {
				if stream.Message != Frozen {
					rec.RecordError(fmt.Errorf("vreplication streams are not frozen on tablet %d", target.GetPrimary().Alias.Uid))
					return nil
				}
			}
			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
}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Restore topology health and re-run completion validation (vtctldclient Workflow --workflow X MoveTablesComplete)
  2. Check the topo server connection parameters and permissions of vtctld
  3. If you intentionally want to keep routing rules, rerun with --keep-routing-rules so the RoutingRules read is skipped

Example fix

// before: read failure recorded but rules used anyway
rules, err := topotools.GetRoutingRules(ctx, ts.TopoServer())
if err != nil {
    rec.RecordError(errors.New("could not get RoutingRules"))
}
// after: stop before using possibly-nil rules
rules, err := topotools.GetRoutingRules(ctx, ts.TopoServer())
if err != nil {
    rec.RecordError(errors.New("could not get RoutingRules"))
    return
}
Defensive patterns

Strategy: validation

Validate before calling

rules, err := topotools.GetRoutingRules(ctx, ts.TopoServer())
if err != nil {
    return fmt.Errorf("topology unreachable, cannot validate routing rules: %w", err)
}

Type guard

if rules == nil {
    return errors.New("routing rules not loaded")
}

Try / catch

rec := &topo.TopoRecorder{}
validateWorkflowHasCompleted(ctx, ts, wf)
for _, e := range rec.ErrorMessages() {
    if strings.Contains(e, "could not get RoutingRules") {
        return fmt.Errorf("fix topo server and retry completion validation: %s", e)
    }
}

Prevention

When it happens

Trigger: Calling validateWorkflowHasCompleted (CompletionValidation for MoveTables) when topotools.GetRoutingRules fails — usually a topology read error (topo server unreachable, etcd/zookeeper down, permission problem) — while keepRoutingRules is false and MigrationType is TABLES.

Common situations: Topology backend outage or degraded quorum during cutover, wrong topo flags on vtctld, stale topo data preventing the RoutingRules global key from being read.

Related errors


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