argoproj/argo-workflows · error

invalid semaphore with both ConfigMapKeyRef and Database

Error message

invalid semaphore with both ConfigMapKeyRef and Database

What it means

getSemaphoreLockName validates a SemaphoreRef and rejects one that sets both ConfigMapKeyRef and Database — a semaphore must resolve to exactly one backend. This is a configuration validation error raised while computing the lock name for a workflow's synchronization entry.

Source

Thrown at workflow/sync/lock_name.go:55

func (ln *lockName) GetNamespace() string    { return ln.namespace }
func (ln *lockName) GetResourceName() string { return ln.resourceName }
func (ln *lockName) GetKey() string          { return ln.key }
func (ln *lockName) getKind() lockKind       { return ln.kind }

func newLockName(namespace, resourceName, lockKey string, kind lockKind) *lockName {
	return &lockName{
		namespace:    namespace,
		resourceName: resourceName,
		key:          lockKey,
		kind:         kind,
	}
}

func getSemaphoreLockName(sem *v1alpha1.SemaphoreRef, wfNamespace string) (*lockName, error) {
	switch {
	case sem.ConfigMapKeyRef != nil && sem.Database != nil:
		return nil, fmt.Errorf("invalid semaphore with both ConfigMapKeyRef and Database")
	case sem.ConfigMapKeyRef != nil:
		namespace := sem.Namespace
		if namespace == "" {
			namespace = wfNamespace
		}
		return newLockName(namespace, sem.ConfigMapKeyRef.Name, sem.ConfigMapKeyRef.Key, lockKindConfigMap), nil
	case sem.Database != nil:
		namespace := sem.Namespace
		if namespace == "" {
			namespace = wfNamespace
		}
		return newLockName(namespace, sem.Database.Key, "", lockKindDatabase), nil
	default:
		return nil, fmt.Errorf("cannot get LockName for a Semaphore without a ConfigMapRef or Database")
	}
}

func getMutexLockName(mtx *v1alpha1.Mutex, wfNamespace string) *lockName {

View on GitHub (pinned to 35bff19146)

Solutions

  1. Remove the `configMapKeyRef` field if you intend database-backed synchronization
  2. Remove the `database` field if you intend ConfigMap-backed synchronization
  3. Lint the workflow before submit (`argo lint`) to catch the ambiguous spec early

Example fix

// before
synchronization:
  semaphore:
    configMapKeyRef:
      name: my-cm
      key: my-key
    database:
      key: my-lock
// after
synchronization:
  semaphore:
    database:
      key: my-lock
Defensive patterns

Strategy: validation

Validate before calling

func validateSemaphore(sem map[string]any) error {
    _, hasCM := sem["configMapKeyRef"]
    _, hasDB := sem["database"]
    if hasCM && hasDB {
        return fmt.Errorf("semaphore must set only one of configMapKeyRef or database")
    }
    if !hasCM && !hasDB {
        return fmt.Errorf("semaphore must set configMapKeyRef or database")
    }
    return nil
}

Type guard

func hasExactlyOneBackend(sem *SemaphoreRef) bool {
    if sem == nil { return false }
    return (sem.ConfigMapKeyRef != nil) != (sem.Database != nil)
}

Try / catch

err := wf.Submit(ctx)
if err != nil && strings.Contains(err.Error(), "both ConfigMapKeyRef and Database") {
    return fmt.Errorf("fix workflow spec: %w", err)
}

Prevention

When it happens

Trigger: A workflow/template specifies synchronization.semaphore with both `configMapKeyRef` and `database` fields populated; validation/lock-name resolution in the controller or lint hits the first switch case.

Common situations: Copy-paste editing a workflow that migrates from ConfigMap-based to database-based synchronization without removing the old field; templating/Helm values merging two sources of a semaphore spec.

Related errors


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