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.

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)

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.