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 soView on GitHub (pinned to 01a25a7d17)
Solutions
- Wait for the active recovery to complete and re-check
- Inspect vtorc's recovery table (audit/recovery UI) for a stuck recovery ID and resolve it
- Ensure only one vtorc instance is active for the cluster (leader election working)
- 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
- Run a single vtorc leader per cluster and verify leader election
- Monitor for recoveries stuck in active state and alert on them
- Clean up stale recovery rows after vtorc crashes
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
- err.Error() (EnableRecovery failure)
- invalid choice for enum
- value must be either a float64 (interpreted as seconds) or a
- flagutil: NewOptionalFlag requires a non-nil parse function
- flagutil: OptionalFlagValue has no parse function; use a con
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/7abb6e9692edd4e2.
Report an issue: GitHub.