vitessio/vitess · error
ErrAmbiguousWorkflow
ErrAmbiguousWorkflow
Error message
%w: found %d workflows in keyspace %s with name %s (active_only = %v); this should be impossible
What it means
GetWorkflow expects a workflow name to identify at most one workflow per keyspace; if the lookup returns more than one match, the result would be ambiguous, so the cluster returns ErrAmbiguousWorkflow wrapped with the count. The 'this should be impossible' wording reflects that workflow names are expected to be unique within a keyspace.
Source
Thrown at go/vt/vtadmin/cluster/cluster.go:2035
return workflow.Workflow.Name == name
},
})
if err != nil {
return nil, err
}
switch len(workflows.Workflows) {
case 0:
msg := "%w for keyspace %s and name %s (active_only = %v)"
if len(workflows.Warnings) > 0 {
return nil, fmt.Errorf(msg+"; warnings: %v", errors.ErrNoWorkflow, keyspace, name, opts.ActiveOnly, workflows.Warnings)
}
return nil, fmt.Errorf(msg, errors.ErrNoWorkflow, keyspace, name, opts.ActiveOnly)
case 1:
return workflows.Workflows[0], nil
default:
return nil, fmt.Errorf("%w: found %d workflows in keyspace %s with name %s (active_only = %v); this should be impossible", errors.ErrAmbiguousWorkflow, len(workflows.Workflows), keyspace, name, opts.ActiveOnly)
}
}
// GetWorkflowsOptions is the set of filtering options for GetWorkflows
// requests.
type GetWorkflowsOptions struct {
ActiveOnly bool
IgnoreKeyspaces sets.Set[string]
}
// GetWorkflows returns a list of Workflows in this cluster, across the given
// keyspaces and filtering according to the options passed in.
//
// If the list of keyspaces to check is empty, then GetWorkflows will use the
// result of GetKeyspaces to search all keyspaces in the cluster. In this case,
// opts.IgnoreKeyspaces is respected.
func (c *Cluster) GetWorkflows(ctx context.Context, keyspaces []string, opts GetWorkflowsOptions) (*vtadminpb.ClusterWorkflows, error) {
span, ctx := trace.NewSpan(ctx, "Cluster.GetWorkflows")View on GitHub (pinned to 01a25a7d17)
Solutions
- List all workflows with GetWorkflows and inspect duplicates for the name
- Delete the stale/duplicate workflow(s) via vtctldclient (DeleteTabletMetadata / Workflow delete) keeping only the intended one
- Re-run the query once duplicates are removed
Example fix
// before
wf, _ := cluster.GetWorkflow(ctx, "ks", "commerce2customer", opts)
// after
wfs, err := cluster.GetWorkflows(ctx, "ks", &cluster.GetWorkflowsOptions{})
// inspect wfs.Workflows for duplicates, clean up stale ones, then call GetWorkflow Defensive patterns
Strategy: try-catch
Validate before calling
wfs, _ := cluster.GetWorkflows(ctx, ks, &cluster.GetWorkflowsOptions{})
if len(wfs.Workflows) > 1 { /* resolve duplicates before calling GetWorkflow */ } Try / catch
wf, err := c.GetWorkflow(ctx, ks, name, opts)
if errors.Is(err, errors.ErrAmbiguousWorkflow) {
// fall back to GetWorkflows and disambiguate by UUID
} Prevention
- Clean up stale workflows after aborted MoveTables/Reshard operations
- Prefer addressing workflows by UUID instead of name where possible
- Alert on duplicate workflow names within a keyspace
When it happens
Trigger: Cluster.GetWorkflow with a name that matches 2+ VReplication workflows in the same keyspace — e.g. leftover/duplicated workflows from interrupted MoveTables/Reshard operations that were not fully cleaned up.
Common situations: Orphaned workflows after a failed or aborted MoveTables/Reshard cleanup; manually re-created workflows reusing a name; GTID/failure during vreplication leaving stale shard-level workflow entries.
Related errors
- value out of range
- GetWorkflows(keyspace = %s, active_only = %v) failed: %w
- not allowed: deny-all security-policy enforced
- not allowed: read-only security-policy enforced
- invalid joined path
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/38e507493127be43.
Report an issue: GitHub.