{"record":{"id":"b73be15df0441deb","repo":"argoproj/argo-workflows","slug":"expected-no-more-than-1-workflow-got-d","errorCode":null,"errorMessage":"expected no more than 1 workflow, got %d","messagePattern":"expected no more than 1 workflow, got (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/controller/controller.go","lineNumber":869,"sourceCode":"\t\tun, ok = obj.(*unstructured.Unstructured)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"object %+v is not an unstructured\", obj)\n\t\t}\n\t\twf, err = util.FromUnstructured(un)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// workflow might still be hydrated\n\t\tif wfc.hydrator.IsHydrated(wf) {\n\t\t\tlogger.WithField(\"uid\", wf.UID).Info(ctx, \"Hydrated workflow encountered\")\n\t\t\terr = wfc.hydrator.Dehydrate(ctx, wf)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"expected no more than 1 workflow, got %d\", l)\n\t}\n\tfor _, version := range versions {\n\t\t// skip delete if offload is live\n\t\tif wf != nil && wf.Status.OffloadNodeStatusVersion == version {\n\t\t\tcontinue\n\t\t}\n\t\tif err := wfc.offloadNodeStatusRepo.Delete(ctx, uid, version); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (wfc *WorkflowController) archivedWorkflowGarbageCollector(ctx context.Context) {\n\tdefer runtimeutil.HandleCrashWithContext(ctx, runtimeutil.PanicHandlers...)\n\n\tlogger := logging.RequireLoggerFromContext(ctx)\n\tlogger = logger.WithField(\"component\", \"archived_wf_garbage_collector\")","sourceCodeStart":851,"sourceCodeEnd":887,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/controller/controller.go#L851-L887","documentation":"deleteOffloadedNodesForWorkflow (workflow/controller/controller.go:869) expects the UID index to hold at most one workflow per UID. If the indexer returns more than one object for the same UID, this error is thrown with the actual count. It signals corrupt/duplicated informer-cache state (the UID is supposed to be unique per object).","triggerScenarios":"wfc.wfInformer.GetIndexer().ByIndex(indexes.UIDIndex, uid) returned 2+ objects — duplicate entries in the informer cache for one UID, typically from a cache inconsistency, a bug in index bookkeeping, or non-standard cache seeding in tests.","commonSituations":"Informer cache corruption after reconnect storms to the API server; test harnesses adding the same UID under different names/namespaces; custom modifications of the UID index function.","solutions":["Restart the workflow controller to rebuild the informer cache; the error is logged and GC retries.","Inspect the UIDIndex index function — it must map exactly one indexer entry per UID.","In tests, ensure no two objects with the same UID are added to the indexer."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Sanity-check the UID index maps one entry per UID\ndups := indexer.ByIndex(indexes.UIDIndex, uid)\nif len(dups) > 1 {\n    log.Fatalf(\"UID %s has %d indexer entries; index function is broken\", uid, len(dups))\n}","typeGuard":null,"tryCatchPattern":"switch l := len(workflows); l {\ncase 0: /* deleted */\ncase 1: /* process */\ndefault:\n    return fmt.Errorf(\"expected no more than 1 workflow, got %d\", l)\n}","preventionTips":["Restart the controller if duplicate indexer entries appear (rebuilds cache)","Verify the UIDIndex index function returns the UID for every object exactly once","Never add two objects sharing a UID to a test indexer"],"tags":["kubernetes","informer","index","gc","duplicate"],"backgroundTag":"informer-index-duplicate-uid","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"}