{"record":{"id":"9013cf89544ec85e","repo":"argoproj/argo-workflows","slug":"cannot-initialize-a-cached-node-from-a-non-memoize","errorCode":null,"errorMessage":"cannot initialize a cached node from a non-memoized template","messagePattern":"cannot initialize a cached node from a non-memoized template","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"workflow/controller/operator.go","lineNumber":3005,"sourceCode":"\n\treturn nodeCtx, node\n}\n\n// initializeNodeOrMarkError initializes an error node or mark a node if it already exists.\nfunc (woc *wfOperationCtx) initializeNodeOrMarkError(ctx context.Context, node *wfv1.NodeStatus, nodeName string, templateScope string, orgTmpl wfv1.TemplateReferenceHolder, boundaryID string, nodeFlag *wfv1.NodeFlag, err error) *wfv1.NodeStatus {\n\tif node != nil {\n\t\treturn woc.markNodeError(ctx, nodeName, err)\n\t}\n\n\t_, n := woc.initializeNode(ctx, nodeName, wfv1.NodeTypeSkipped, templateScope, orgTmpl, boundaryID, wfv1.NodeError, nodeFlag, true, err.Error())\n\treturn n\n}\n\n// Creates a node status that is or will be cached\n// Returns the context with the node span and the node status.\nfunc (woc *wfOperationCtx) initializeCacheNode(ctx context.Context, nodeName string, resolvedTmpl *wfv1.Template, templateScope string, orgTmpl wfv1.TemplateReferenceHolder, boundaryID string, memStat *wfv1.MemoizationStatus, nodeFlag *wfv1.NodeFlag, messages ...string) (context.Context, *wfv1.NodeStatus) {\n\tif resolvedTmpl.Memoize == nil {\n\t\terr := fmt.Errorf(\"cannot initialize a cached node from a non-memoized template\")\n\t\twoc.log.WithFields(logging.Fields{\"namespace\": woc.wf.Namespace, \"wfName\": woc.wf.Name}).WithError(err)\n\t\tpanic(err)\n\t}\n\twoc.log.WithFields(logging.Fields{\n\t\t\"nodeName\":       nodeName,\n\t\t\"templateHolder\": common.GetTemplateHolderString(orgTmpl),\n\t\t\"boundaryID\":     boundaryID,\n\t},\n\t).Debug(ctx, \"Initializing cached node\")\n\n\tnodeCtx, node := woc.initializeExecutableNode(ctx, nodeName, wfutil.GetNodeType(resolvedTmpl), templateScope, resolvedTmpl, orgTmpl, boundaryID, wfv1.NodePending, nodeFlag, false, messages...)\n\tnode.MemoizationStatus = memStat\n\treturn nodeCtx, node\n}\n\n// Creates a node status that has been cached, completely initialized, and marked as finished\n// Returns the context with the node span and the node status.\nfunc (woc *wfOperationCtx) initializeCacheHitNode(ctx context.Context, nodeName string, resolvedTmpl *wfv1.Template, templateScope string, orgTmpl wfv1.TemplateReferenceHolder, boundaryID string, outputs *wfv1.Outputs, memStat *wfv1.MemoizationStatus, nodeFlag *wfv1.NodeFlag, messages ...string) (context.Context, *wfv1.NodeStatus) {","sourceCodeStart":2987,"sourceCodeEnd":3023,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/controller/operator.go#L2987-L3023","documentation":"Argo Workflows' memoization feature caches node results for templates that declare a `memoize:` section. `initializeCacheNode` (workflow/controller/operator.go:3005) is only ever supposed to be called for templates that participate in memoization; if the resolved template has `Memoize == nil`, the controller treats it as an unrecoverable internal invariant violation and panics instead of returning an error. This means the workflow-controller binary crashed (and the pod restarted) rather than just failing the node.","triggerScenarios":"Reached only when the controller resolves a template reference (templateRef, template template, or steps/DAG entry) whose cached/memoization status is being initialized — e.g. a memoization key lookup path — but the resolved template ends up without `memoize:` set. Concrete causes: (1) a template ref points to a template that lost its `memoize:` block after the node ID was computed, so the stored node expects memoization but the new template doesn't have it; (2) a controller bug where `executeTemplate` computes `memStat` for a template it did not verify has `Memoize != nil`; (3) editing a workflow spec between retries/resubmits so a previously memoized node now resolves against a non-memoized template.","commonSituations":"Users upgrade Argo or edit a cluster workflow template and remove/rename the `memoize:` key while workflows referencing it are still running; users hand-edit submitted workflow specs; developers extending memoization to new template types call initializeCacheNode/initializeCacheHitNode with a template whose Memoize field was never validated.","solutions":["Restore the `memoize:` block (with `key:` and `maxAge:`) on the template being referenced — the resolved template must be memoized for cached-node initialization","Do not edit/delete the ClusterWorkflowTemplate or WorkflowTemplate that running memoized workflows resolve against; delete and resubmit the affected workflow instead","If you are a developer: guard the caller with `if resolvedTmpl.Memoize == nil { return nil, errors.New(...) }` instead of relying on the panic, and ensure executeTemplate only computes memoization status when Memoize is set","Restart/recover the workflow-controller pod (kubectl rollout restart) if it crashed from this panic, then fix the spec before retrying"],"exampleFix":"// before (workflow spec)\ntemplates:\n- name: build          # memoize removed in an edit\n  container:\n    image: golang\n// after\ntemplates:\n- name: build\n  memoize:\n    key: build-cache\n    maxAge: \"24h\"\n  container:\n    image: golang","handlingStrategy":"validation","validationCode":"// validate the referenced template before submitting/updating\ntmpl := resolvedTemplate\nif tmpl.Memoize == nil && expectsMemoization {\n    return fmt.Errorf(\"template %q must define memoize.key and memoize.maxAge\", tmpl.Name)\n}\n// also: never mutate a ClusterWorkflowTemplate that running workflows memoize against","typeGuard":"func isMemoizable(tmpl *wfv1.Template) bool {\n    return tmpl != nil && tmpl.Memoize != nil && tmpl.Memoize.Key != \"\"\n}","tryCatchPattern":"null","preventionTips":["Run `argo lint` on every workflow/template change","Treat WorkflowTemplates referenced by running workflows as immutable; create new versions instead of editing","When adding memoization, always include both `key` and `maxAge`","If writing controller code, check Memoize != nil before calling initializeCacheNode/initializeCacheHitNode"],"tags":["argo-workflows","memoization","controller-panic","workflow-templates"],"backgroundTag":"memoization-without-memoize-template","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"}