vitessio/vitess · error

can't get primary replication position: %v

Error message

can't get primary replication position: %v

What it means

getPrimaryPosition calls tmc.PrimaryPosition on the primary tablet to get its current replication position. If the TabletManager RPC fails, this error wraps the cause. It means Vitess reached (or tried to reach) the primary tablet but could not obtain its position.

Source

Thrown at go/vt/mysqlctl/builtinbackupengine.go:1841

}

func (be *BuiltinBackupEngine) Name() string { return builtinBackupEngineName }

func getPrimaryPosition(ctx context.Context, tmc tmclient.TabletManagerClient, ts *topo.Server, keyspace, shard string) (replication.Position, error) {
	si, err := ts.GetShard(ctx, keyspace, shard)
	if err != nil {
		return replication.Position{}, vterrors.Wrap(err, "can't read shard")
	}
	if topoproto.TabletAliasIsZero(si.PrimaryAlias) {
		return replication.Position{}, fmt.Errorf("shard %v/%v has no primary", keyspace, shard)
	}
	ti, err := ts.GetTablet(ctx, si.PrimaryAlias)
	if err != nil {
		return replication.Position{}, fmt.Errorf("can't get primary tablet record %v: %v", topoproto.TabletAliasString(si.PrimaryAlias), err)
	}
	posStr, err := tmc.PrimaryPosition(ctx, ti.Tablet)
	if err != nil {
		return replication.Position{}, fmt.Errorf("can't get primary replication position: %v", err)
	}
	pos, err := replication.DecodePosition(posStr)
	if err != nil {
		return replication.Position{}, fmt.Errorf("can't decode primary replication position %q: %v", posStr, err)
	}
	return pos, nil
}

func init() {
	BackupRestoreEngineMap[builtinBackupEngineName] = &BuiltinBackupEngine{}
}

// closeWithRetry does just what it says. Retrying a close operation is important as
// an error is most likely transient/ephemeral and leaving around open file descriptors
// can lead to later problems as the file may be in a sort-of uploaded state where it
// exists but has not yet been finalized (this is true for GCS). This can cause
// unexpected behavior if you retry the file while the original request is still in this
// state. Most implementations such as GCS will automatically retry operations, but close

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Check the primary tablet's health and gRPC port (vtctldclient GetTablet, then hit /debug vars).
  2. Verify connectivity/auth (certs, grpc port) between the caller and the tablet.
  3. Confirm mysqld is running on the primary so the agent can read SHOW REPLICA STATUS / binlog position.
  4. Retry once the tablet is healthy — the failure is often transient.
Defensive patterns

Strategy: retry

Validate before calling

// check tablet reachability before the RPC
if err := checkTabletHealth(ctx, ti.Tablet); err != nil {
	return fmt.Errorf("primary tablet unhealthy, skipping position fetch: %w", err)
}

Try / catch

pos, err := getPrimaryPosition(ctx, tmc, ts, ks, shard)
if err != nil && strings.Contains(err.Error(), "replication position") {
	// transient tablet RPC failure: retry with backoff
	pos, err = retryWithBackoff(ctx, func() (replication.Position, error) {
		return getPrimaryPosition(ctx, tmc, ts, ks, shard)
	})
}

Prevention

When it happens

Trigger: tmc.PrimaryPosition(ctx, ti.Tablet) returns an error — tablet agent down, RPC timeout, TLS/auth mismatch between vtctld/vtworker and the tablet, or mysqld unreachable from the tablet agent.

Common situations: Primary tablet process restarting during backup; network partition or firewall blocking the tablet's gRPC port; tablet agent cannot query mysqld for the position.

Related errors


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