argoproj/argo-workflows · error

output parameters must have a valueFrom specified

Error message

output parameters must have a valueFrom specified

What it means

When the controller resolves a step/DAG template's output parameters from the execution scope (workflow/controller/operator.go:3463), every output parameter must declare how its value is produced via `valueFrom` (parameter, path, expression, default, etc.). A parameter with a `name` but no `valueFrom` has no way to be evaluated, so template resolution fails immediately and the node is marked errored. This check runs before the template's containers even start, catching invalid specs at execution time (full validation also happens at submit via workflow/validate).

Source

Thrown at workflow/controller/operator.go:3463

	}
	outbound := make([]string, 0)
	for _, outboundNodeID := range node.OutboundNodes {
		outbound = append(outbound, woc.getOutboundNodes(ctx, outboundNodeID)...)
	}
	return outbound
}

// getTemplateOutputsFromScope resolves a template's outputs from the scope of the template
func (woc *wfOperationCtx) getTemplateOutputsFromScope(ctx context.Context, tmpl *wfv1.Template, scope *wfScope) (*wfv1.Outputs, error) {
	if !tmpl.Outputs.HasOutputs() {
		return nil, nil
	}
	var outputs wfv1.Outputs
	if len(tmpl.Outputs.Parameters) > 0 {
		outputs.Parameters = make([]wfv1.Parameter, 0)
		for _, param := range tmpl.Outputs.Parameters {
			if param.ValueFrom == nil {
				return nil, fmt.Errorf("output parameters must have a valueFrom specified")
			}
			val, skipped, err := scope.resolveParameter(param.ValueFrom)
			if err != nil {
				// We have a default value to use instead of returning an error
				if param.ValueFrom.Default == nil {
					return nil, err
				}
				val = param.ValueFrom.Default.String()
			} else if skipped && param.ValueFrom.Default != nil {
				// The referenced step was skipped/omitted and produced no output; use the declared default.
				val = param.ValueFrom.Default.String()
			}
			if val == nil {
				// Skipped/omitted output with no default anywhere — neither the producer's
				// valueFrom.default nor this aggregating parameter's own — is an unhandled absent
				// optional: fail terminally, mirroring simple-tag substitution semantics.
				return nil, argoerrors.Errorf(argoerrors.CodeBadRequest, "output parameter %q: %q is an absent optional (skipped/omitted node output with no default)", param.Name, param.ValueFrom.Parameter)
			}

View on GitHub (pinned to 35bff19146)

Solutions

  1. Add a `valueFrom` to each output parameter, e.g. `valueFrom: {path: /tmp/result.txt}` or `valueFrom: {parameter: '{{steps.build.outputs.parameters.result}}'}`
  2. Lint the workflow before submitting (`argo lint file.yaml`) — the validator reports missing valueFrom before execution
  3. Verify YAML indentation so `valueFrom` is a child of the parameter, not of `outputs.parameters`
  4. If the value is constant, use `valueFrom: {default: myvalue}` instead of a bare value

Example fix

# before
outputs:
  parameters:
  - name: result
    value: /tmp/out.txt
# after
outputs:
  parameters:
  - name: result
    valueFrom:
      path: /tmp/out.txt
Defensive patterns

Strategy: validation

Validate before calling

# lint before submit — catches missing valueFrom
# $ argo lint my-workflow.yaml
# programmatic check:
for _, p := range tmpl.Outputs.Parameters {
    if p.ValueFrom == nil {
        return fmt.Errorf("output parameter %q must specify valueFrom", p.Name)
    }
}

Type guard

func hasValueFrom(p wfv1.Parameter) bool {
    return p.ValueFrom != nil &&
        (p.ValueFrom.Path != "" || p.ValueFrom.Parameter != "" ||
         p.ValueFrom.Expression != "" || p.ValueFrom.JQFilter != "" ||
         p.ValueFrom.JSONPath != "" || p.ValueFrom.Default != nil)
}

Try / catch

null

Prevention

When it happens

Trigger: A workflow spec defines `outputs.parameters` entries lacking `valueFrom` — e.g. hand-written YAML where the valueFrom block was omitted or mis-indented so it parsed as a sibling key; or a template generated programmatically (SDK/JSON) that only sets name/value.

Common situations: Copy-pasting a parameter output example and deleting the valueFrom block; YAML indentation mistakes nesting valueFrom under the wrong key; building specs with the Python/Java SDK and forgetting ValueFrom; upgrading from very old Argo versions where some valueFrom variants were optional.

Related errors


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