vitessio/vitess · error

AttemptRecoveryRegistration: Active recovery (id:%v) in the

Error message

AttemptRecoveryRegistration: Active recovery (id:%v) in the cluster %s:%s for %s

What it means

AttemptRecoveryRegistration found an already-active (unresolved) topology recovery in the same keyspace/shard, so it refuses to register a duplicate concurrent recovery. Only one recovery per cluster shard may run at a time.

Source

Thrown at go/vt/vtorc/logic/topology_recovery_dao.go:136

	if err != nil {
		return nil, err
	}
	topologyRecovery.ID = lastInsertID
	return topologyRecovery, nil
}

// AttemptRecoveryRegistration tries to add a recovery entry; if this fails that means recovery is already in place.
func AttemptRecoveryRegistration(analysisEntry *inst.DetectionAnalysis) (*TopologyRecovery, error) {
	// Check if there is an active recovery in progress for the cluster of the given instance.
	recoveries, err := ReadActiveClusterRecoveries(analysisEntry.AnalyzedKeyspace, analysisEntry.AnalyzedShard)
	if err != nil {
		log.Error(err.Error())
		return nil, err
	}
	if len(recoveries) > 0 {
		errMsg := fmt.Sprintf("AttemptRecoveryRegistration: Active recovery (id:%v) in the cluster %s:%s for %s", recoveries[0].ID, analysisEntry.AnalyzedKeyspace, analysisEntry.AnalyzedShard, recoveries[0].AnalysisEntry.Analysis)
		log.Error(errMsg)
		return nil, errors.New(errMsg)
	}

	topologyRecovery := NewTopologyRecovery(*analysisEntry)

	topologyRecovery, err = writeTopologyRecovery(topologyRecovery)
	if err != nil {
		log.Error(err.Error())
		return nil, err
	}
	return topologyRecovery, nil
}

// ResolveRecovery is called on completion of a recovery process and updates the recovery status.
// It does not clear the "active period" as this still takes place in order to avoid flapping.
// The recovery_detection row is NOT deleted here: if the recovery failed, the problem is still
// active and the detection row must survive so subsequent retry attempts share the same
// detection_id. The incident boundary is established by resolveRecovery when IsSuccessful=true
// (i.e. a new primary was promoted via ERS/PRS); at that point the detection row is deleted so

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Wait for the active recovery to complete and re-check
  2. Inspect vtorc's recovery table (audit/recovery UI) for a stuck recovery ID and resolve it
  3. Ensure only one vtorc instance is active for the cluster (leader election working)
  4. If the active recovery is genuinely stale/crashed, clean it up per your vtorc maintenance procedure
Defensive patterns

Strategy: try-catch

Try / catch

_, err := logic.AttemptRecoveryRegistration(ctx, entry, expireTime, ActionRecoverPrimary)
if err != nil && strings.Contains(err.Error(), "Active recovery") {
    // another recovery owns this shard; back off
    return nil
}
if err != nil { return err }

Prevention

When it happens

Trigger: Multiple analysis entries (primary failure, replica issues) trigger recoveries for the same cluster simultaneously; a prior recovery is still marked active in the vtorc database because it never completed or was not marked done.

Common situations: Stale recovery rows in the _vt.recovery table after vtorc crash; overlapping detections such as DeadPrimary plus PrimaryReadOnly hitting fixPrimary and runPlannedReparentOp at once; multiple vtorc instances competing.

Related errors


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