{"record":{"id":"58d4619fd1845ce1","repo":"tursodatabase/turso","slug":"client-closed","errorCode":"CLIENT_CLOSED","errorMessage":"Client is closed","messagePattern":"Client is closed","errorType":"exception","errorClass":"LibsqlError","httpStatus":null,"severity":"error","filePath":"serverless/javascript/src/compat.ts","lineNumber":298,"sourceCode":"    if (options != null && typeof options === \"object\") {\n      return {\n        mode: options.mode,\n        raw: options.raw === true,\n      };\n    }\n    return {\n      mode: options,\n      raw: false,\n    };\n  }\n\n  async execute(stmt: InStatement): Promise<ResultSet>;\n  async execute(sql: string, args?: InArgs): Promise<ResultSet>;\n  async execute(stmtOrSql: InStatement | string, args?: InArgs): Promise<ResultSet> {\n    await this.execLock.acquire();\n    try {\n      if (this._closed) {\n        throw new LibsqlError(\"Client is closed\", \"CLIENT_CLOSED\");\n      }\n\n      let normalizedStmt: { sql: string; args: any[] };\n\n      if (typeof stmtOrSql === 'string') {\n        const normalizedArgs = args ? (Array.isArray(args) ? args : Object.values(args)) : [];\n        normalizedStmt = { sql: stmtOrSql, args: normalizedArgs };\n      } else {\n        normalizedStmt = this.normalizeStatement(stmtOrSql);\n      }\n\n      const result = await this.session.execute(normalizedStmt.sql, normalizedStmt.args, this._defaultSafeIntegers);\n      return this.convertResult(result);\n    } catch (error: any) {\n      if (error instanceof LibsqlError) {\n        throw error;\n      }\n      throw mapDatabaseError(error, \"EXECUTE_ERROR\");","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/serverless/javascript/src/compat.ts#L280-L316","documentation":"execute() on the compatibility-layer client acquires the exec lock and then checks the _closed flag, throwing a LibsqlError with code CLIENT_CLOSED if the client was closed. close() is synchronous: it flips _closed immediately and closes the underlying session fire-and-forget, so any execute() issued after close() rejects with this error rather than reaching the server.","triggerScenarios":"Sequencing like client.close(); await client.execute(sql). A shared module-level client that one request or test closes (e.g. in afterEach) while another concurrent request still calls execute(). A close in a finally block followed by a retry of the same query.","commonSituations":"Test suites that close a singleton client in teardown while later tests reuse it; serverless warm invocations reusing a client that a previous invocation closed during cleanup; race conditions where cleanup code closes the client before an in-flight promise calls execute.","solutions":["Do not reuse the client after close() — create a fresh one with createClient(config) for the next unit of work","Check the public client.closed getter before issuing queries and recreate the client when it is true","Move close() out of per-request paths; only close at process exit or when the owner that created the client is done","Fix ordering bugs where close() runs before a pending execute() promise settles"],"exampleFix":"// before\nclient.close();\nconst rs = await client.execute('SELECT 1'); // LibsqlError CLIENT_CLOSED\n\n// after\nif (client.closed) {\n  client = createClient(config);\n}\nconst rs = await client.execute('SELECT 1');","handlingStrategy":"validation","validationCode":"async function executeOrRecreate(sql: string) {\n  if (client.closed) {\n    client = createClient(config);\n  }\n  return client.execute(sql);\n}","typeGuard":"const isUsable = (c: Client): boolean => !c.closed;","tryCatchPattern":"try {\n  const rs = await client.execute(sql);\n} catch (e) {\n  if (e instanceof LibsqlError && e.code === 'CLIENT_CLOSED') {\n    client = createClient(config);\n    return client.execute(sql);\n  }\n  throw e;\n}","preventionTips":["Check the public client.closed getter before issuing queries on shared clients","Close clients only in the scope that created them, at true end-of-life (process exit)","Await all in-flight work before calling close() to avoid racing a settling execute()","In tests, create a fresh client per suite instead of closing a global singleton in afterEach"],"tags":["lifecycle","client-closed","serverless","use-after-close"],"backgroundTag":"use-after-close","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}