{"record":{"id":"f88310962f4769eb","repo":"argoproj/argo-workflows","slug":"output-parameters-must-have-a-valuefrom-specified","errorCode":null,"errorMessage":"output parameters must have a valueFrom specified","messagePattern":"output parameters must have a valueFrom specified","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/controller/operator.go","lineNumber":3463,"sourceCode":"\t}\n\toutbound := make([]string, 0)\n\tfor _, outboundNodeID := range node.OutboundNodes {\n\t\toutbound = append(outbound, woc.getOutboundNodes(ctx, outboundNodeID)...)\n\t}\n\treturn outbound\n}\n\n// getTemplateOutputsFromScope resolves a template's outputs from the scope of the template\nfunc (woc *wfOperationCtx) getTemplateOutputsFromScope(ctx context.Context, tmpl *wfv1.Template, scope *wfScope) (*wfv1.Outputs, error) {\n\tif !tmpl.Outputs.HasOutputs() {\n\t\treturn nil, nil\n\t}\n\tvar outputs wfv1.Outputs\n\tif len(tmpl.Outputs.Parameters) > 0 {\n\t\toutputs.Parameters = make([]wfv1.Parameter, 0)\n\t\tfor _, param := range tmpl.Outputs.Parameters {\n\t\t\tif param.ValueFrom == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"output parameters must have a valueFrom specified\")\n\t\t\t}\n\t\t\tval, skipped, err := scope.resolveParameter(param.ValueFrom)\n\t\t\tif err != nil {\n\t\t\t\t// We have a default value to use instead of returning an error\n\t\t\t\tif param.ValueFrom.Default == nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tval = param.ValueFrom.Default.String()\n\t\t\t} else if skipped && param.ValueFrom.Default != nil {\n\t\t\t\t// The referenced step was skipped/omitted and produced no output; use the declared default.\n\t\t\t\tval = param.ValueFrom.Default.String()\n\t\t\t}\n\t\t\tif val == nil {\n\t\t\t\t// Skipped/omitted output with no default anywhere — neither the producer's\n\t\t\t\t// valueFrom.default nor this aggregating parameter's own — is an unhandled absent\n\t\t\t\t// optional: fail terminally, mirroring simple-tag substitution semantics.\n\t\t\t\treturn 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)\n\t\t\t}","sourceCodeStart":3445,"sourceCodeEnd":3481,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/controller/operator.go#L3445-L3481","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Add a `valueFrom` to each output parameter, e.g. `valueFrom: {path: /tmp/result.txt}` or `valueFrom: {parameter: '{{steps.build.outputs.parameters.result}}'}`","Lint the workflow before submitting (`argo lint file.yaml`) — the validator reports missing valueFrom before execution","Verify YAML indentation so `valueFrom` is a child of the parameter, not of `outputs.parameters`","If the value is constant, use `valueFrom: {default: myvalue}` instead of a bare value"],"exampleFix":"# before\noutputs:\n  parameters:\n  - name: result\n    value: /tmp/out.txt\n# after\noutputs:\n  parameters:\n  - name: result\n    valueFrom:\n      path: /tmp/out.txt","handlingStrategy":"validation","validationCode":"# lint before submit — catches missing valueFrom\n# $ argo lint my-workflow.yaml\n# programmatic check:\nfor _, p := range tmpl.Outputs.Parameters {\n    if p.ValueFrom == nil {\n        return fmt.Errorf(\"output parameter %q must specify valueFrom\", p.Name)\n    }\n}","typeGuard":"func hasValueFrom(p wfv1.Parameter) bool {\n    return p.ValueFrom != nil &&\n        (p.ValueFrom.Path != \"\" || p.ValueFrom.Parameter != \"\" ||\n         p.ValueFrom.Expression != \"\" || p.ValueFrom.JQFilter != \"\" ||\n         p.ValueFrom.JSONPath != \"\" || p.ValueFrom.Default != nil)\n}","tryCatchPattern":"null","preventionTips":["Run `argo lint` on all manifests before submit","Keep valueFrom correctly indented as a child of the parameter entry","Use `valueFrom.default` for constant values rather than a bare `value`","Copy from a known-good output-parameter example rather than writing YAML from scratch"],"tags":["argo-workflows","workflow-spec","output-parameters","validation"],"backgroundTag":"missing-valuefrom-on-output-parameter","analyzedSha":"35bff19146f5a6ada77468c431f2624bd577e373","analyzedAt":"2026-09-03T19:34:35.908Z","contentChangedAt":"2026-09-03T19:34:35.908Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}