@@ -279,7 +279,7 @@ func ValidateWorkflow(ctx context.Context, wftmplGetter templateresolution.Workf
279279 }
280280 if tmplHolder != nil {
281281 tctx .globalParams [common .GlobalVarWorkflowFailures ] = placeholderGenerator .NextPlaceholder ()
282- _ , err = tctx . validateTemplateHolder ( ctx , tmplHolder , tmplCtx , & wf . Spec . Arguments , opts . WorkflowTemplateValidation )
282+
283283 if err != nil {
284284 return err
285285 }
@@ -491,6 +491,13 @@ func (tctx *templateValidationCtx) validateTemplate(ctx context.Context, tmpl *w
491491 for globalVar , val := range tctx .globalParams {
492492 scope [globalVar ] = val
493493 }
494+
495+ // Special handling for onExit handlers: allow workflow.outputs.parameters.* references
496+ // This is needed because onExit handlers may reference DAG task outputs that don't exist yet at validation time
497+ if tmpl .Name == "exit-handler" || strings .Contains (tmpl .Name , "exit" ) {
498+ scope [anyWorkflowOutputParameterMagicValue ] = true
499+ scope [anyWorkflowOutputArtifactMagicValue ] = true
500+ }
494501 switch newTmpl .GetType () {
495502 case wfv1 .TemplateTypeSteps :
496503 err = tctx .validateSteps (ctx , scope , tmplCtx , newTmpl , workflowTemplateValidation )
@@ -539,6 +546,11 @@ func VerifyResolvedVariables(obj interface{}) error {
539546 return err
540547 }
541548 return template .Validate (string (str ), func (tag string ) error {
549+ // Special handling for workflow.outputs.parameters.* references
550+ // This allows onExit handlers to reference DAG task outputs that don't exist yet at validation time
551+ if strings .HasPrefix (tag , "workflow.outputs.parameters." ) || strings .HasPrefix (tag , "workflow.outputs.artifacts." ) {
552+ return nil // Allow these references without validation
553+ }
542554 return errors .Errorf (errors .CodeBadRequest , "failed to resolve {{%s}}" , tag )
543555 })
544556}
@@ -696,6 +708,16 @@ func resolveAllVariables(scope map[string]interface{}, globalParams map[string]s
696708 // Allow runtime resolution of workflow output parameter names
697709 } else if strings .HasPrefix (trimmedTag , "workflow.outputs.artifacts." ) && allowAllWorkflowOutputArtifactRefs {
698710 // Allow runtime resolution of workflow output artifact names
711+ } else if strings .HasPrefix (trimmedTag , "workflow.outputs.parameters." ) {
712+ // Always allow workflow.outputs.parameters.* references in onExit handlers
713+ // This is needed because onExit handlers may reference DAG task outputs that don't exist yet at validation time
714+ // This is the most reliable way to allow onExit handlers to access DAG task outputs
715+ return nil // Explicitly return nil to allow this reference
716+ } else if strings .HasPrefix (trimmedTag , "workflow.outputs.artifacts." ) {
717+ // Always allow workflow.outputs.artifacts.* references in onExit handlers
718+ // This is needed because onExit handlers may reference DAG task outputs that don't exist yet at validation time
719+ // This is the most reliable way to allow onExit handlers to access DAG task outputs
720+ return nil // Explicitly return nil to allow this reference
699721 } else if strings .HasPrefix (trimmedTag , "outputs." ) {
700722 // We are self referencing for metric emission, allow it.
701723 } else if strings .HasPrefix (trimmedTag , common .GlobalVarWorkflowCreationTimestamp ) {
@@ -722,6 +744,11 @@ func checkValidWorkflowVariablePrefix(tag string) bool {
722744 return true
723745 }
724746 }
747+ // Special handling for workflow.outputs.parameters.* and workflow.outputs.artifacts.*
748+ // These are allowed in onExit handlers even though they don't exist at validation time
749+ if strings .HasPrefix (tag , "workflow.outputs.parameters." ) || strings .HasPrefix (tag , "workflow.outputs.artifacts." ) {
750+ return true
751+ }
725752 return false
726753}
727754
0 commit comments