n8n-io/n8n · critical · OperationalError

Timed out after ${timeoutMs}ms waiting for database connecti

Error message

Timed out after ${timeoutMs}ms waiting for database connection recovery

What it means

OperationalError thrown when DB connection recovery does not complete within connectionAcquisitionTimeoutMs. The awaitConnection method races the pending recovery promise against a setTimeout; if the timeout (when > 0) wins, this error fires. AbortController aborts the timer if recovery completes first. This is the hard cap on how long a caller waits for the DB to come back.

Source

Thrown at packages/@n8n/db/src/connection/db-connection-monitor.ts:617

	private async awaitRecovery() {
		const pending = this.pendingRecovery;
		if (!pending) {
			return;
		}

		const timeoutMs = this.databaseConfig.connectionAcquisitionTimeoutMs;
		if (timeoutMs <= 0) {
			await pending;
			return;
		}

		// AbortController clears the timeout timer once recovery (or stop) wins the race
		const abortController = new AbortController();
		try {
			await Promise.race([
				pending,
				setTimeoutP(timeoutMs, undefined, { signal: abortController.signal }).then(() => {
					throw new OperationalError(
						`Timed out after ${timeoutMs}ms waiting for database connection recovery`,
					);
				}),
			]);
		} finally {
			abortController.abort();
		}
	}

	private startRecovery() {
		this.recovering = true;
		this.markRecoveryPending();
	}

	private markRecoveryPending() {
		this.pendingRecovery ??= new Promise<void>(
			(resolve) => (this.resolvePendingRecovery = resolve),
		);

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Raise connectionAcquisitionTimeoutMs to exceed your worst-case DB recovery SLO (e.g. failover time).
  2. Ensure the DB is reachable and healthy: check Postgres logs, `pg_isready`, and that the host/port in config are correct.
  3. If deploying in orchestrated environments, add a startup readiness wait or initContainer that probes Postgres before n8n starts.
  4. For failover scenarios, verify the connection string points at the right primary and DNS updates have propagated.
  5. Investigate recovery stalls: check DbConnectionMonitor logs for whether recovery started at all.

Example fix

// before
DB_CONNECTION_ACQUISITION_TIMEOUT_MS=5000

// after
DB_CONNECTION_ACQUISITION_TIMEOUT_MS=60000
Defensive patterns

Strategy: retry

Validate before calling

const ok = await probeDbRecovery(connectionAcquisitionTimeoutMs);
if (!ok) { /* do not proceed; alert ops, the DB has not recovered in budget */ }

Type guard

function isRecoveryTimeout(err: unknown): boolean {
  return err instanceof OperationalError && /^Timed out after \d+ms waiting for database connection recovery/.test(err.message);
}

Try / catch

try {
  await awaitConnection();
} catch (err) {
  if (isRecoveryTimeout(err)) {
    // enter degraded mode or fail open per policy; do not tight-loop retry
  } else throw err;
}

Prevention

When it happens

Trigger: A prolonged DB outage (Postgres down, restarted, or unreachable) where recovery hasn't restored a usable connection within connectionAcquisitionTimeoutMs. The error surfaces to any code path that calls awaitConnection during the outage window.

Common situations: Postgres maintenance window longer than the configured timeout; a failover that exceeds the budget; a deadlock in recovery logic; misconfigured timeout far shorter than realistic recovery time; startup during a DB that isn't ready yet.

Understand the failure class

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/8ac0b391b6dd96bd. Report an issue: GitHub.