argoproj/argo-workflows · critical

synchronization database session is not available

Error message

synchronization database session is not available

What it means

One of the lock keys requires a database-backed semaphore, but the Manager was built without a DB session proxy (dbInfo.SessionProxy == nil). Without the session the manager cannot read or update DB lock state, so acquisition is refused rather than silently bypassing the DB lock.

Solutions

  1. Enable and configure synchronization DB persistence in the controller config
  2. Point the controller at the correct Postgres/MySQL instance used for sync state
  3. Restart the controller so the session proxy is initialized
  4. Switch the workflow to ConfigMap-based synchronization if no DB is intended

Example fix

// before (controller config)
# no sync database configured
// after
syncDatabase:
  postgresql:
    host: postgres
    port: 5432
    database: argo_sync
Defensive patterns

Strategy: validation

Validate before calling

if needsDB(lockKeys) && manager.SessionProxy == nil {
    return errors.New("configure sync DB persistence before using DB-backed semaphores")
}

Type guard

func dbSessionReady(sm *sync.Manager) bool { return sm != nil && sm.DBInfo != nil && sm.DBInfo.SessionProxy != nil }

Try / catch

if _, _, _, _, err := sm.TryAcquire(...); err != nil && strings.Contains(err.Error(), "database session is not available") { /* alert: persistence misconfigured */ }

Prevention

When it happens

Trigger: A workflow uses a database-sourced semaphore (sync config in Postgres/MySQL) while the controller runs without synchronization DB persistence configured or the session is unavailable at runtime.

Common situations: Controller installed with persistence disabled while workflows reference DB-backed sync; wrong DB credentials so SessionProxy was never created; DB lost after startup.

Related errors


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

Appendix: source

Thrown at workflow/sync/sync_manager.go:484

	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)
	}

	if ok, msg, prepLockName, prepErr := sm.prepAcquire(ctx, wf, holderKey, syncItems, lockKeys); !ok {
		return false, false, msg, prepLockName, prepErr
	}

	needDB, err := needDBSession(ctx, lockKeys)
	if err != nil {
		return false, false, "", failedLockName, fmt.Errorf("couldn't decode locks for session: %w", err)
	}
	if needDB && sm.dbInfo.SessionProxy == nil {
		return false, false, "", failedLockName, fmt.Errorf("synchronization database session is not available")
	}
	if needDB {
		var updated bool
		var already bool
		var msg string
		var newly []*acquiredLock
		backoff := dbRetryBackoff
		// tryAcquireImpl mutates wf.Status.Synchronization in memory before the
		// transaction commits. Snapshot it and roll each failed attempt back, so
		// that attempts are independent and an error return leaves the caller's
		// status exactly as it was - otherwise an abort leaves a Holding entry
		// for a row the database rolled back, which would be persisted and then
		// failed as a stale hold on the next controller restart.
		syncStatus := wf.Status.Synchronization.DeepCopy()
		attempt := 0
		err = retry.OnError(backoff, IsRetryableSyncError, func() error {
			attempt++
			sm.log.WithFields(logging.Fields{

View on GitHub (pinned to 35bff19146)