{"record":{"id":"caf5e76f82557854","repo":"argoproj/argo-workflows","slug":"failed-to-get-current-lock-holders-w","errorCode":null,"errorMessage":"failed to get current lock holders: %w","messagePattern":"failed to get current lock holders: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/sync/sync_manager.go","lineNumber":657,"sourceCode":"\t\tvar newly []*acquiredLock\n\t\tfor i, lockKey := range lockKeys {\n\t\t\tlock := sm.syncLockMap[lockKey]\n\t\t\tvar acquired bool\n\t\t\tvar acquireErr error\n\t\t\tacquired, msg, acquireErr = lock.tryAcquire(ctx, holderKey, tx)\n\t\t\tif acquireErr != nil {\n\t\t\t\t// Surface the underlying error so callers (e.g. TryAcquire's\n\t\t\t\t// retry loop) can decide whether it is retryable. Transient\n\t\t\t\t// database errors like PostgreSQL SQLSTATE 40001 must reach\n\t\t\t\t// the retry detector untouched.\n\t\t\t\treturn false, false, \"\", failedLockName, nil, acquireErr\n\t\t\t}\n\t\t\tif !acquired {\n\t\t\t\treturn false, false, \"\", failedLockName, nil, fmt.Errorf(\"bug: failed to acquire something that should have been checked: %s\", msg)\n\t\t\t}\n\t\t\tcurrentHolders, err := sm.getCurrentLockHolders(ctx, lockKey)\n\t\t\tif err != nil {\n\t\t\t\treturn false, false, \"\", failedLockName, nil, fmt.Errorf(\"failed to get current lock holders: %w\", err)\n\t\t\t}\n\t\t\tif wf.Status.Synchronization.GetStatus(syncItems[i].getType()).LockAcquired(holderKey, lockKey, currentHolders) {\n\t\t\t\tupdated = true\n\t\t\t\tnewly = append(newly, &acquiredLock{name: lockKey, kind: syncItems[i].getType()})\n\t\t\t}\n\t\t}\n\t\treturn true, updated, msg, failedLockName, newly, nil\n\tdefault: // Not all acquirable\n\t\tupdated := false\n\t\tfor i, lockKey := range lockKeys {\n\t\t\tcurrentHolders, err := sm.getCurrentLockHolders(ctx, lockKey)\n\t\t\tif err != nil {\n\t\t\t\treturn false, false, \"\", failedLockName, nil, fmt.Errorf(\"failed to get current lock holders: %w\", err)\n\t\t\t}\n\t\t\tif wf.Status.Synchronization.GetStatus(syncItems[i].getType()).LockWaiting(holderKey, lockKey, currentHolders) {\n\t\t\t\tupdated = true\n\t\t\t}\n\t\t}","sourceCodeStart":639,"sourceCodeEnd":675,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/sync/sync_manager.go#L639-L675","documentation":"After acquiring, tryAcquireImpl reads the authoritative holder list via getCurrentLockHolders to reconcile workflow status; a backend error here aborts the acquire with 'failed to get current lock holders'. Lock state is unknown, so the operation fails rather than risking an inconsistent double-acquire.","triggerScenarios":"getCurrentLockHolders errors after a successful individual acquire — ConfigMap read/decode failure, DB query failure, or transient backend unavailability for that lockKey.","commonSituations":"API-server throttling or RBAC denial on ConfigMap reads; Postgres/MySQL outage for DB-backed semaphores; oversized/corrupted sync ConfigMap.","solutions":["Check the wrapped cause: ConfigMap RBAC vs DB connectivity","Retry the operation — often transient (backend hiccup)","Verify the controller's RBAC allows reading sync ConfigMaps","Check DB health for database-backed locks"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"failed to get current lock holders\") {\n    if utilerrors.IsTransientErr(ctx, err) { time.Sleep(backoff); retry() }\n}","preventionTips":["Grant the controller get/update on sync ConfigMaps","Configure retry/backoff around TryAcquire in operator loops","Monitor sync backend (DB/API server) health"],"tags":["argo-workflows","sync","backend","transient","database"],"backgroundTag":"lock-holder-lookup-failed","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"}