{"record":{"id":"6b462a42df19c333","repo":"argoproj/argo-workflows","slug":"workflowspec-may-not-change-during-execution-when","errorCode":null,"errorMessage":"WorkflowSpec may not change during execution when the controller is set `templateReferencing: Secure`","messagePattern":"WorkflowSpec may not change during execution when the controller is set `templateReferencing: Secure`","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/controller/operator.go","lineNumber":4641,"sourceCode":"\t\t\treturn err\n\t\t}\n\t\twoc.wf.Status.StoredWorkflowSpec = &mergedWf.Spec\n\t\twoc.updated = true\n\t} else if woc.controller.Config.WorkflowRestrictions.MustNotChangeSpec() {\n\t\twftHolder, err := woc.fetchWorkflowSpec(ctx)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tuserSpec := &woc.wf.Spec //nolint:forbidigo // not-woc-misuse\n\t\tif woc.controller.Config.WorkflowRestrictions.MustUseReference() {\n\t\t\tuserSpec = wfutil.SanitizeUserWorkflowSpec(&woc.wf.Spec) //nolint:forbidigo // not-woc-misuse\n\t\t}\n\t\tmergedWf, err := wfutil.JoinWorkflowSpec(userSpec, wftHolder.GetWorkflowSpec(), &wfDefault.Spec)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif mergedWf.Spec.String() != woc.wf.Status.StoredWorkflowSpec.String() {\n\t\t\treturn fmt.Errorf(\"WorkflowSpec may not change during execution when the controller is set `templateReferencing: Secure`\")\n\t\t}\n\t}\n\treturn nil\n}\n\n// mergedTemplateDefaultsInto modifies originalTmpl, setting any applicable default values.\nfunc (woc *wfOperationCtx) mergedTemplateDefaultsInto(originalTmpl *wfv1.Template) error {\n\tif woc.execWf.Spec.TemplateDefaults == nil {\n\t\treturn nil\n\t}\n\n\toriginalTmplType := originalTmpl.GetType()\n\tapplicableDefaults := woc.execWf.Spec.TemplateDefaults.DeepCopy()\n\n\tv := reflect.ValueOf(applicableDefaults).Elem()\n\tfor i := 0; i < v.NumField(); i++ {\n\t\tfield := v.Type().Field(i)\n\t\t// Check if the field is a pointer to a struct.","sourceCodeStart":4623,"sourceCodeEnd":4659,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/controller/operator.go#L4623-L4659","documentation":"In Secure template-referencing mode, the controller re-joins the user spec with the WorkflowTemplate's spec on every operation and compares it to the spec stored at submission. Any divergence means the effective spec would change mid-run, so reconciliation fails the workflow with this error.","triggerScenarios":"During reconciliation in templateReferencing: Secure mode, JoinWorkflowSpec(userSpec, templateSpec, defaults) produces a spec whose serialized form differs from Status.StoredWorkflowSpec.","commonSituations":"Someone edits the WorkflowTemplate while a workflow is running; a controller upgrade changes defaulting behavior so the merged output differs; manual kubectl edits to the running Workflow's spec.","solutions":["Restore the WorkflowTemplate content to what it was when the workflow was submitted","Do not modify the Workflow's spec after submission; resubmit a new workflow instead","Check for controller version changes in defaulting; pin/upgrade consistently and retry with a new workflow"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// before editing, confirm no running workflow uses the template\nkubectl get wf -n ns -o json | jq '.items[] | select(.status.storedWorkflowSpec != null)'","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never edit a WorkflowTemplate while workflows referencing it are running","Never patch a running Workflow's spec","Use immutable, versioned templates (e.g. name-v1) and roll forward"],"tags":["kubernetes","argo-workflows","immutability","secure-mode"],"backgroundTag":"spec-mutated-during-execution","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"}