argoproj/argo-workflows · error

400

400

Error message

workflow must be Failed/Error to resubmit in memoized mode

What it means

FormulateResubmitWorkflow in workflow/util/util.go builds a new workflow spec when resubmitting. In memoized mode the new run reuses the original node statuses, which is only meaningful for workflows that already reached a terminal failure. If wf.Status.Phase is anything other than WorkflowFailed or WorkflowWorkflowError when memoized is true, it returns a coded 400 (errors.CodeBadRequest) refusing the resubmit.

Source

Thrown at workflow/util/util.go:767

func FormulateResubmitWorkflow(ctx context.Context, wf *wfv1.Workflow, memoized bool, parameters []string) (*wfv1.Workflow, error) {
	log := logging.RequireLoggerFromContext(ctx)
	newWF := wfv1.Workflow{}
	newWF.TypeMeta = wf.TypeMeta

	// Resubmitted workflow will use generated names
	if wf.GenerateName != "" {
		newWF.GenerateName = wf.GenerateName
	} else {
		newWF.GenerateName = wf.Name + "-"
	}
	// When resubmitting workflow with memoized nodes, we need to use a predetermined workflow name
	// in order to formulate the node statuses. Which means we cannot reuse metadata.generateName
	// The following simulates the behavior of generateName
	if memoized {
		switch wf.Status.Phase {
		case wfv1.WorkflowFailed, wfv1.WorkflowError:
		default:
			return nil, errors.Errorf(errors.CodeBadRequest, "workflow must be Failed/Error to resubmit in memoized mode")
		}
		newWF.Name = newWF.GenerateName + RandSuffix()
	}

	// carry over the unmodified spec
	newWF.Spec = wf.Spec

	if newWF.Spec.ActiveDeadlineSeconds != nil && *newWF.Spec.ActiveDeadlineSeconds == 0 {
		// if it was terminated, unset the deadline
		newWF.Spec.ActiveDeadlineSeconds = nil
	}

	newWF.Spec.Shutdown = ""

	// carry over user labels and annotations from previous workflow.
	if newWF.Labels == nil {
		newWF.Labels = make(map[string]string)
	}

View on GitHub (pinned to 35bff19146)

Solutions

  1. Wait until the workflow reaches Failed or Error phase before resubmitting with --memoized (poll `argo get <wf>` or watch phase).
  2. Drop the --memoized flag to do a plain resubmit of a running/succeeded workflow.
  3. If the goal is rerunning parts of a succeeded workflow, use `argo retry --restart-successful --node-field-selector ...` instead.

Example fix

# before
argo resubmit my-wf --memoized        # my-wf is Running
# after
argo wait my-wf && argo resubmit my-wf --memoized   # only if it ended Failed/Error
# or, without memoization:
argo resubmit my-wf
Defensive patterns

Strategy: validation

Validate before calling

// Go: check phase before memoized resubmit
if memoized && wf.Status.Phase != wfv1.WorkflowFailed && wf.Status.Phase != wfv1.WorkflowError {
	return fmt.Errorf("workflow %s is %s; memoized resubmit requires Failed/Error", wf.Name, wf.Status.Phase)
}

Try / catch

// Go: catch the 400 and fall back to a plain resubmit
newWF, err := util.FormulateResubmitWorkflow(ctx, wf, "", true)
if err != nil {
	if argoErr, ok := err.(errors.ArgoError); ok && argoErr.Code() == errors.CodeBadRequest {
		newWF, err = util.FormulateResubmitWorkflow(ctx, wf, "", false) // non-memoized
	}
}

Prevention

When it happens

Trigger: Calling FormulateResubmitWorkflow (via ResubmitWorkflow / ResubmitArchivedWorkflow, or `argo resubmit --memoized`) on a workflow whose status.phase is Running, Pending, Succeeded, or empty while the memoized option is enabled.

Common situations: Running `argo resubmit <wf> --memoized` on a currently running workflow; resubmitting a succeeded workflow with --memoized expecting incremental behavior (that is retry's job, not resubmit's); scripting that always passes memoized=true without checking phase.

Related errors


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/b20a3c2b292820aa. Report an issue: GitHub.