{"record":{"id":"8ac0b391b6dd96bd","repo":"n8n-io/n8n","slug":"timed-out-after-timeoutms-ms-waiting-for-databas","errorCode":null,"errorMessage":"Timed out after ${timeoutMs}ms waiting for database connection recovery","messagePattern":"Timed out after (.+?)ms waiting for database connection recovery","errorType":"exception","errorClass":"OperationalError","httpStatus":null,"severity":"critical","filePath":"packages/@n8n/db/src/connection/db-connection-monitor.ts","lineNumber":617,"sourceCode":"\tprivate async awaitRecovery() {\n\t\tconst pending = this.pendingRecovery;\n\t\tif (!pending) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst timeoutMs = this.databaseConfig.connectionAcquisitionTimeoutMs;\n\t\tif (timeoutMs <= 0) {\n\t\t\tawait pending;\n\t\t\treturn;\n\t\t}\n\n\t\t// AbortController clears the timeout timer once recovery (or stop) wins the race\n\t\tconst abortController = new AbortController();\n\t\ttry {\n\t\t\tawait Promise.race([\n\t\t\t\tpending,\n\t\t\t\tsetTimeoutP(timeoutMs, undefined, { signal: abortController.signal }).then(() => {\n\t\t\t\t\tthrow new OperationalError(\n\t\t\t\t\t\t`Timed out after ${timeoutMs}ms waiting for database connection recovery`,\n\t\t\t\t\t);\n\t\t\t\t}),\n\t\t\t]);\n\t\t} finally {\n\t\t\tabortController.abort();\n\t\t}\n\t}\n\n\tprivate startRecovery() {\n\t\tthis.recovering = true;\n\t\tthis.markRecoveryPending();\n\t}\n\n\tprivate markRecoveryPending() {\n\t\tthis.pendingRecovery ??= new Promise<void>(\n\t\t\t(resolve) => (this.resolvePendingRecovery = resolve),\n\t\t);","sourceCodeStart":599,"sourceCodeEnd":635,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/db/src/connection/db-connection-monitor.ts#L599-L635","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Raise connectionAcquisitionTimeoutMs to exceed your worst-case DB recovery SLO (e.g. failover time).","Ensure the DB is reachable and healthy: check Postgres logs, `pg_isready`, and that the host/port in config are correct.","If deploying in orchestrated environments, add a startup readiness wait or initContainer that probes Postgres before n8n starts.","For failover scenarios, verify the connection string points at the right primary and DNS updates have propagated.","Investigate recovery stalls: check DbConnectionMonitor logs for whether recovery started at all."],"exampleFix":"// before\nDB_CONNECTION_ACQUISITION_TIMEOUT_MS=5000\n\n// after\nDB_CONNECTION_ACQUISITION_TIMEOUT_MS=60000","handlingStrategy":"retry","validationCode":"const ok = await probeDbRecovery(connectionAcquisitionTimeoutMs);\nif (!ok) { /* do not proceed; alert ops, the DB has not recovered in budget */ }","typeGuard":"function isRecoveryTimeout(err: unknown): boolean {\n  return err instanceof OperationalError && /^Timed out after \\d+ms waiting for database connection recovery/.test(err.message);\n}","tryCatchPattern":"try {\n  await awaitConnection();\n} catch (err) {\n  if (isRecoveryTimeout(err)) {\n    // enter degraded mode or fail open per policy; do not tight-loop retry\n  } else throw err;\n}","preventionTips":["Set connectionAcquisitionTimeoutMs above your DB failover SLO.","Probe DB readiness in your deployment before n8n boots (initContainer / healthcheck).","Plan for recovery: have a degraded-mode behavior ready rather than blocking callers indefinitely."],"tags":["database","postgres","timeout","recovery","transient"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}