argoproj/argo-workflows · error

workflow cannot be nil

Error message

workflow cannot be nil

What it means

ApplySubmitOpts mutates a *wfv1.Workflow with CLI/server submit options; it requires a non-nil workflow pointer. A nil wf is a programming error by the caller (the workflow was never formulated/loaded), so it fails fast with this error rather than panicking.

Source

Thrown at workflow/util/util.go:249

func PopulateSubmitOpts(command *cobra.Command, submitOpts *wfv1.SubmitOpts, parameterFile *string, includeDryRun bool) {
	command.Flags().StringVar(&submitOpts.Name, "name", "", "override metadata.name")
	command.Flags().StringVar(&submitOpts.GenerateName, "generate-name", "", "override metadata.generateName")
	command.Flags().StringVar(&submitOpts.Entrypoint, "entrypoint", "", "override entrypoint")
	command.Flags().StringArrayVarP(&submitOpts.Parameters, "parameter", "p", []string{}, "pass an input parameter")
	command.Flags().StringVar(&submitOpts.ServiceAccount, "serviceaccount", "", "run all pods in the workflow using specified serviceaccount")
	command.Flags().StringVarP(parameterFile, "parameter-file", "f", "", "pass a file containing all input parameters")
	command.Flags().StringVarP(&submitOpts.Labels, "labels", "l", "", "Comma separated labels to apply to the workflow. Will override previous values.")

	if includeDryRun {
		command.Flags().BoolVar(&submitOpts.DryRun, "dry-run", false, "modify the workflow on the client-side without creating it")
		command.Flags().BoolVar(&submitOpts.ServerDryRun, "server-dry-run", false, "send request to server with dry-run flag which will modify the workflow without creating it")
	}
}

// ApplySubmitOpts applies the submit options to a workflow object.
func ApplySubmitOpts(wf *wfv1.Workflow, opts *wfv1.SubmitOpts) error {
	if wf == nil {
		return fmt.Errorf("workflow cannot be nil")
	}
	if opts == nil {
		opts = &wfv1.SubmitOpts{}
	}
	if opts.Entrypoint != "" {
		wf.Spec.Entrypoint = opts.Entrypoint
	}
	if opts.ServiceAccount != "" {
		wf.Spec.ServiceAccountName = opts.ServiceAccount
	}
	if opts.PodPriorityClassName != "" {
		wf.Spec.PodPriorityClassName = opts.PodPriorityClassName
	}

	if opts.Priority != nil {
		wf.Spec.Priority = opts.Priority
	}

View on GitHub (pinned to 35bff19146)

Solutions

  1. Ensure the workflow is successfully created/parsed before calling ApplySubmitOpts and propagate earlier errors
  2. Add a nil check on the result of the formulate/parse step before applying submit options
  3. If using CLI, verify the file/URL/- arguments actually produce a workflow (argo submit with valid input)

Example fix

// before
wf, err := util.FromFile(...) // err ignored
_ = util.ApplySubmitOpts(wf, opts) // wf is nil
// after
wf, err := util.FromFile(...)
if err != nil { return err }
if err := util.ApplySubmitOpts(wf, opts); err != nil { return err }
Defensive patterns

Strategy: type-guard

Validate before calling

if wf == nil {
    return fmt.Errorf("cannot submit: workflow is nil, check formulate/parse step")
}

Type guard

func workflowReady(wf *wfv1.Workflow) bool {
    return wf != nil && wf.Spec.Entrypoint != ""
}

Try / catch

if err := util.ApplySubmitOpts(wf, opts); err != nil {
    if strings.Contains(err.Error(), "workflow cannot be nil") {
        return fmt.Errorf("bug: workflow not constructed before ApplySubmitOpts: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling ApplySubmitOpts(nil, opts) directly, or passing a workflow that a prior formulate/parse step failed to produce (caller ignored an earlier error and passed a nil *wfv1.Workflow) from CreateCronWorkflows, updateCronWorkflows, submitWorkflows, or SubmitWorkflow.

Common situations: Custom tooling built on workflow/util that skips workflow formulation; error from unmarshal/FromFile ignored so wf stays nil; refactoring that reordered code so ApplySubmitOpts runs before the wf is created.

Related errors


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