argoproj/argo-workflows · error

cannot acquire lock from nil Synchronization

Error message

cannot acquire lock from nil Synchronization

What it means

Manager.TryAcquire requires a non-nil *wfv1.Synchronization describing what to acquire. A nil reference means the caller passed no synchronization/mutex spec at all, so the call is rejected immediately. This is a programming/configuration bug in the caller, not a transient condition.

Source

Thrown at workflow/sync/sync_manager.go:455

				}
				if reason != "" {
					stale(reason)
				}
			}
		}
	}
	sm.log.Info(ctx, "Sync manager initialized successfully")
	return staleHolds, nil
}

// TryAcquire tries to acquire the lock from semaphore.
// It returns status of acquiring a lock , status of Workflow status updated, waiting message if lock is not available, the failed lock, and any error encountered
func (sm *Manager) TryAcquire(ctx context.Context, wf *wfv1.Workflow, nodeName string, syncLockRef *wfv1.Synchronization) (bool, bool, string, string, error) {
	sm.lock.Lock()
	defer sm.lock.Unlock()

	if syncLockRef == nil {
		return false, false, "", "", fmt.Errorf("cannot acquire lock from nil Synchronization")
	}

	failedLockName := ""
	syncItems, err := allSyncItems(syncLockRef)
	if err != nil {
		return false, false, "", failedLockName, fmt.Errorf("requested configuration is invalid: %w", err)
	}
	holderKey := getHolderKey(wf, nodeName)

	lockKeys := make([]string, len(syncItems))
	for i, syncItem := range syncItems {
		syncLockName, lockNameErr := syncItem.lockName(wf.Namespace)
		if lockNameErr != nil {
			return false, false, "", failedLockName, fmt.Errorf("requested configuration is invalid: %w", lockNameErr)
		}
		sm.log.WithField("syncLockName", syncLockName).Info(ctx, "TryAcquire")
		lockKeys[i] = syncLockName.String(ctx)
	}

View on GitHub (pinned to 35bff19146)

Solutions

  1. Check the template declares synchronization.semaphore or synchronization.mutex before calling TryAcquire
  2. Skip the TryAcquire call when the node has no synchronization reference
  3. Verify the workflow spec was fully unmarshaled with no dropped fields

Example fix

// before
ok, _, msg, lock, err := sm.TryAcquire(ctx, wf, nodeName, nil)
// after
if tmpl.Synchronization != nil {
    ok, _, msg, lock, err := sm.TryAcquire(ctx, wf, nodeName, tmpl.Synchronization)
}
Defensive patterns

Strategy: validation

Validate before calling

if syncRef == nil {
    // node has no synchronization; do not call TryAcquire
    return
}

Type guard

func hasSync(tmpl *wfv1.Template) bool {
    return tmpl != nil && tmpl.Synchronization != nil
}

Try / catch

if _, _, _, _, err := sm.TryAcquire(ctx, wf, node, ref); err != nil && strings.Contains(err.Error(), "nil Synchronization") { /* skip: no sync declared */ }

Prevention

When it happens

Trigger: Calling TryAcquire with syncLockRef == nil, e.g. when the node's template declares neither semaphore nor mutex but the operator loop still invokes TryAcquire.

Common situations: Custom controller code calling TryAcquire without checking whether the template declares a synchronization; partial YAML unmarshal dropping the synchronization field.

Related errors


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/ef2adef61a49e326. Report an issue: GitHub.