argoproj/argo-workflows · error · ArgoError

BadRequest

BadRequest

Error message

templates.%s.script.image may not be empty

What it means

The `default` branch of the script-image check: if the current template base is neither a Workflow nor a WorkflowTemplate (e.g. a ClusterWorkflowTemplate or unknown base kind), the validator cannot consult templateDefaults, so an empty `script.image` is unconditionally rejected. Ensures every script template ends up with a resolvable container image.

Source

Thrown at workflow/validate/validate.go:974

				if err != nil {
					return errors.Errorf(errors.CodeBadRequest, "templates.%s.resource.manifest must be a valid yaml", tmpl.Name)
				}
			}
		}
	}
	if tmpl.Script != nil {
		if tmpl.Script.Image == "" {
			switch baseTemplate := tmplCtx.GetCurrentTemplateBase().(type) {
			case *wfv1.Workflow:
				if baseTemplate.Spec.TemplateDefaults == nil || baseTemplate.Spec.TemplateDefaults.Script == nil || baseTemplate.Spec.TemplateDefaults.Script.Image == "" {
					return errors.Errorf(errors.CodeBadRequest, "templates.%s.script.image may not be empty", tmpl.Name)
				}
			case *wfv1.WorkflowTemplate:
				if baseTemplate.Spec.TemplateDefaults == nil || baseTemplate.Spec.TemplateDefaults.Script == nil || baseTemplate.Spec.TemplateDefaults.Script.Image == "" {
					return errors.Errorf(errors.CodeBadRequest, "templates.%s.script.image may not be empty", tmpl.Name)
				}
			default:
				return errors.Errorf(errors.CodeBadRequest, "templates.%s.script.image may not be empty", tmpl.Name)
			}
		}
	}
	// we don't validate tmpl.Plugin, because this is done by Plugin.UnmarshallJSON
	if tmpl.ActiveDeadlineSeconds != nil {
		if !intstr.IsValidIntOrArgoVariable(tmpl.ActiveDeadlineSeconds) && !placeholderGenerator.IsPlaceholder(tmpl.ActiveDeadlineSeconds.StrVal) {
			return errors.Errorf(errors.CodeBadRequest, "templates.%s.activeDeadlineSeconds must be a positive integer > 0 or an argo variable", tmpl.Name)
		}
		if i, err := intstr.Int(tmpl.ActiveDeadlineSeconds); err == nil && i != nil && *i < 0 {
			return errors.Errorf(errors.CodeBadRequest, "templates.%s.activeDeadlineSeconds must be a positive integer > 0 or an argo variable", tmpl.Name)
		}
	}
	if tmpl.Parallelism != nil {
		return errors.Errorf(errors.CodeBadRequest, "templates.%s.parallelism is only valid for steps and dag templates", tmpl.Name)
	}
	return nil
}

View on GitHub (pinned to 35bff19146)

Solutions

  1. Always set `script.image` explicitly on script templates referenced via ClusterWorkflowTemplate.
  2. Move the script template into a WorkflowTemplate/Workflow where templateDefaults can be consulted, and set defaults there.
  3. Check `tmplCtx.GetCurrentTemplateBase()` resolution if you believe the kind is wrong.

Example fix

// before
script:
  source: echo hi
// after
script:
  image: alpine:3.19
  source: echo hi
Defensive patterns

Strategy: validation

Validate before calling

if (tmpl.script && !tmpl.script.image) {
  throw new Error('script.image must be explicit when base kind is not Workflow/WorkflowTemplate (e.g. ClusterWorkflowTemplate)');
}

Type guard

function scriptImageExplicit(t) {
  return !t.script || Boolean(t.script.image);
}

Prevention

When it happens

Trigger: A script template without `image` validated against a base template kind other than Workflow/WorkflowTemplate — typically when templates come from ClusterWorkflowTemplates or the template context returns an unexpected type.

Common situations: Referencing a ClusterWorkflowTemplate that itself lacks a script image and has no defaults honored by this check; custom/older CRD kinds passing through validation.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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