vitessio/vitess · error

failed to open external topo

Error message

failed to open external topo: %v

What it means

When ms.ExternalCluster is set, the source lives on an externally mounted MySQL/Vitess cluster and buildMaterializer must open that cluster's topo server via OpenExternalVitessClusterServer. This error wraps any failure opening the external topo server, making the source topology unreachable.

Solutions

  1. Run `vtctl Mount` (or re-mount) the external cluster and confirm the name matches ms.ExternalCluster
  2. Verify connectivity to the external topo server (host, port, credentials)
  3. List mounts with `vtctl ListMounts` to confirm the cluster exists

Example fix

// before
MoveTables --external-cluster=prod_ext_x --source-keyspace=ks
// after
vtctl Mount --topo-type=etcd2 --topo-server=host:2379 ext0
MoveTables --external-cluster=ext0 --source-keyspace=ks
Defensive patterns

Strategy: validation

Validate before calling

mounts, err := ts.ListMounts(ctx)
if err != nil { return err }
if !containsMount(mounts, externalCluster) {
    return fmt.Errorf("mount %q not found; run Mount first", externalCluster)
}

Try / catch

if err := createWorkflow(ctx, req); err != nil {
    if strings.Contains(err.Error(), "failed to open external topo") {
        // check ListMounts, connectivity, and re-Mount the cluster
    }
    return err
}

Prevention

When it happens

Trigger: Workflow creation (createWorkflowStreams / WorkflowAddTables) with --external-cluster set while the external cluster name is wrong, the mount is stale, or the external topo server is unreachable.

Common situations: Cluster unmounted (or name typo) before workflow creation; network/firewall blocking the external topo; external topo credentials changed; stale mount after the external cluster was re-created.

Related errors


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

Appendix: source

Thrown at go/vt/vtctl/workflow/materializer.go:553

	}
	if len(specifiedTargetShards) > 0 {
		var targetShards2 []*topo.ShardInfo
		for _, shard := range targetShards {
			if slices.Contains(specifiedTargetShards, shard.ShardName()) {
				targetShards2 = append(targetShards2, shard)
			}
		}
		targetShards = targetShards2
	}
	if len(targetShards) == 0 {
		return fmt.Errorf("no target shards specified for workflow %s ", ms.Workflow)
	}

	sourceTs := mz.ts
	if ms.ExternalCluster != "" { // when the source is an external mysql cluster mounted using the Mount command
		externalTopo, err := mz.ts.OpenExternalVitessClusterServer(ctx, ms.ExternalCluster)
		if err != nil {
			return fmt.Errorf("failed to open external topo: %v", err)
		}
		sourceTs = externalTopo
	}
	differentPVs := false
	sourceVSchema, err := sourceTs.GetVSchema(ctx, ms.SourceKeyspace)
	if err != nil {
		return fmt.Errorf("failed to get source keyspace vschema: %v", err)
	}
	differentPVs = primaryVindexesDiffer(ms, sourceVSchema.Keyspace, vschema.Keyspace)

	mz.targetVSchema = targetVSchema
	mz.sourceShards = sourceShards
	mz.targetShards = targetShards
	mz.isPartial = isPartial
	mz.primaryVindexesDiffer = differentPVs
	return nil
}

View on GitHub (pinned to 01a25a7d17)