{"record":{"id":"16bde295fc70b073","repo":"argoproj/argo-workflows","slug":"holderkey-is-empty","errorCode":null,"errorMessage":"holderkey is empty","messagePattern":"holderkey is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/sync/sync_manager.go","lineNumber":110,"sourceCode":"\t\tsyncLimitCacheTTL: syncLimitCacheTTL,\n\t\tworkflowExists:    workflowExists,\n\t\tdbInfo:            dbInfo,\n\t\tqueries:           syncdb.NewSyncQueries(sessionProxy, dbInfo.Config),\n\t\tlog:               log,\n\t}\n\tlog.WithField(\"dbConfigured\", sm.dbInfo.SessionProxy != nil).Info(ctx, \"Sync manager initialized\")\n\tsm.dbInfo.Migrate(ctx)\n\n\tif sm.dbInfo.SessionProxy != nil {\n\t\tsm.backgroundNotifier(ctx, config.PollSeconds)\n\t\tsm.dbControllerHeartbeat(ctx, config.HeartbeatSeconds)\n\t}\n\treturn sm\n}\n\nfunc (sm *Manager) getWorkflowKey(key string) (string, error) {\n\tif key == \"\" {\n\t\treturn \"\", fmt.Errorf(\"holderkey is empty\")\n\t}\n\titems := strings.Split(key, \"/\")\n\tif len(items) < 2 {\n\t\treturn \"\", fmt.Errorf(\"invalid holderkey format\")\n\t}\n\treturn fmt.Sprintf(\"%s/%s\", items[0], items[1]), nil\n}\n\nfunc (sm *Manager) CheckWorkflowExistence(ctx context.Context) {\n\tdefer runtimeutil.HandleCrashWithContext(ctx, runtimeutil.PanicHandlers...)\n\n\tsm.lock.Lock()\n\tdefer sm.lock.Unlock()\n\n\tsm.log.Debug(ctx, \"Check the workflow existence\")\n\tfor _, lock := range sm.syncLockMap {\n\t\tholders, err := lock.getCurrentHolders(ctx)\n\t\tif err != nil {","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/sync/sync_manager.go#L92-L128","documentation":"Manager.getWorkflowKey parses a holder key of the form '<namespace>/<workflow>/<node>' down to '<namespace>/<workflow>'. An empty holder key cannot identify any workflow, so this error is returned and used by CheckWorkflowExistence to decide whether the holder's workflow still exists.","triggerScenarios":"CheckWorkflowExistence iterates holders of a lock and passes an empty string as the key — typically a holder entry recorded with no holderKey (corrupted sync state, or a semaphore/mutex acquired with an unset key).","commonSituations":"Status/sync maps corrupted after a crash or manual edit of workflow status; code paths creating holds before the holder key is computed; database rows with empty holder columns.","solutions":["Inspect the workflow's synchronization status for empty holder entries and clean them up (re-submit or patch the workflow)","Check the sync database/ConfigMap state for blank holder keys and remove them","If reproducible, audit the code path that acquires the lock to ensure holderKey is always set before registration"],"exampleFix":"// before: holder registered with empty key\nholderKey := \"\"\nsm.tryAcquire(lockName, holderKey, ...)\n// after\nholderKey := fmt.Sprintf(\"%s/%s/%s\", wf.Namespace, wf.Name, nodeName)\nsm.tryAcquire(lockName, holderKey, ...)","handlingStrategy":"validation","validationCode":"func validateHolderKey(key string) error {\n    if key == \"\" { return fmt.Errorf(\"holderkey is empty\") }\n    if strings.Count(key, \"/\") < 2 { return fmt.Errorf(\"holderkey must be namespace/workflow/node\") }\n    return nil\n}","typeGuard":"func isHolderKey(s string) bool {\n    parts := strings.Split(s, \"/\")\n    return len(parts) >= 2 && parts[0] != \"\" && parts[1] != \"\"\n}","tryCatchPattern":"err := acquire(ctx, lockName, holderKey)\nif err != nil && strings.Contains(err.Error(), \"holderkey is empty\") {\n    return fmt.Errorf(\"holder key must be computed before acquiring: %w\", err)\n}","preventionTips":["Always build holder keys with namespace/workflow/node via one shared helper","Reject empty names early when constructing synchronization requests","Avoid manual edits to sync state that can blank holder fields"],"tags":["semaphore","holder-key","sync-manager"],"backgroundTag":"empty-holder-key","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"}