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

  1. List all workflows with GetWorkflows and inspect duplicates for the name
  2. Delete the stale/duplicate workflow(s) via vtctldclient (DeleteTabletMetadata / Workflow delete) keeping only the intended one
  3. 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

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


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