{"record":{"id":"ea8177dd7ead0a9b","repo":"hcengineering/platform","slug":"db-client-is-already-closed","errorCode":null,"errorMessage":"DB client is already closed","messagePattern":"DB client is already closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"foundations/core/packages/postgres-base/src/index.ts","lineNumber":166,"sourceCode":"export class ClientRef implements PostgresClientReference {\n  id = ++clId\n  constructor (\n    readonly client: PostgresClientReferenceImpl,\n    readonly mgr: ConnectionMgr\n  ) {\n    clientRefs.set(this.id, this)\n  }\n\n  url (): string {\n    return this.client.url()\n  }\n\n  closed = false\n  async getClient (): Promise<postgres.Sql> {\n    if (!this.closed) {\n      return this.client.getClient()\n    } else {\n      throw Error('DB client is already closed')\n    }\n  }\n\n  close (): void {\n    // Do not allow double close of mongo connection client\n    if (!this.closed) {\n      clientRefs.delete(this.id)\n      this.closed = true\n      this.client.close()\n    }\n  }\n}\n\nexport let dbExtraOptions: Partial<Options<any>> = {}\nexport function setDBExtraOptions (options: Partial<Options<any>>): void {\n  dbExtraOptions = options\n}\n","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/core/packages/postgres-base/src/index.ts#L148-L184","documentation":"getClient on the postgres-base connection wrapper throws this Error when the connection has already been closed (`closed === true`). After close() is called, the underlying postgres.Sql client is no longer usable, so any attempt to obtain a client is rejected. It guards against using or double-closing a shut-down DB connection.","triggerScenarios":"Calling getClient (foundations/core/packages/postgres-base/src/index.ts:166) on a connection instance after close() has been invoked on it — e.g., during shutdown handlers, hot-reload, or when a cached connection reference outlives its close call.","commonSituations":"Application shutdown code closing the DB while in-flight requests still call getClient; tests that close a shared connection in afterEach while other async work continues; service restart/reconnect logic holding a stale singleton; double teardown from both framework lifecycle and explicit close.","solutions":["Guard with the public `closed` property before calling getClient, and re-create the connection if it was closed","Do not call close() until all pending operations complete; await outstanding work before teardown","Ensure singleton connection holders are updated (set to null/recreated) after close so stale references aren't reused","In tests, use a fresh connection per test or only close after all awaited queries finish"],"exampleFix":"// before\nconst client = await conn.getClient() // throws if closed\n// after\nif (conn.closed) {\n  conn = createConnection(config)\n}\nconst client = await conn.getClient()","handlingStrategy":"type-guard","validationCode":"if (conn.closed) {\n  conn = recreateConnection(config)\n}\nconst client = await conn.getClient()","typeGuard":"function isConnectionOpen(conn: { closed: boolean }): boolean {\n  return !conn.closed\n}","tryCatchPattern":"let client: postgres.Sql\ntry {\n  client = await conn.getClient()\n} catch (e) {\n  if (e instanceof Error && e.message === 'DB client is already closed') {\n    conn = recreateConnection(config)\n    client = await conn.getClient()\n  } else {\n    throw e\n  }\n}","preventionTips":["Check the `closed` flag before reusing cached connection references","Null out or recreate singletons after calling close()","Await all in-flight queries before teardown/shutdown","In tests, scope connection close to afterAll and never share closed instances"],"tags":["database","postgres","lifecycle","connection-closed"],"backgroundTag":"db-client-closed","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}