{"id":"b7c35e7ded274575","repo":"typeorm/typeorm","slug":"database-connection-provided-by-a-query-runner-was","errorCode":null,"errorMessage":"Database connection provided by a query runner was already released, cannot continue to use its querying methods anymore.","messagePattern":"Database connection provided by a query runner was already released, cannot continue to use its querying methods anymore\\.","errorType":"exception","errorClass":"QueryRunnerProviderAlreadyReleasedError","httpStatus":null,"severity":"error","filePath":"src/data-source/DataSource.ts","lineNumber":515,"sourceCode":"    /**\n     * Executes raw SQL query and returns raw database results.\n     *\n     * @param query\n     * @param parameters\n     * @param queryRunner\n     * @returns a raw response from the database client\n     * @see {@link https://typeorm.io/data-source-api | Official docs} for examples.\n     */\n    async query<T = any>(\n        query: string,\n        parameters?: any[] | ObjectLiteral,\n        queryRunner?: QueryRunner,\n    ): Promise<T> {\n        if (InstanceChecker.isMongoEntityManager(this.manager))\n            throw new TypeORMError(`Queries aren't supported by MongoDB.`)\n\n        if (queryRunner?.isReleased)\n            throw new QueryRunnerProviderAlreadyReleasedError()\n\n        const usedQueryRunner = queryRunner ?? this.createQueryRunner()\n\n        try {\n            return await usedQueryRunner.query(query, parameters) // await is needed here because we are using finally\n        } finally {\n            if (!queryRunner) await usedQueryRunner.release()\n        }\n    }\n\n    /**\n     * Tagged template function that executes raw SQL query and returns raw database results.\n     * Template expressions are automatically transformed into database parameters.\n     * Raw query execution is supported only by relational databases (MongoDB is not supported).\n     * Note: Don't call this as a regular function, it is meant to be used with backticks to tag a template literal.\n     *\n     * @example\n     * dataSource.sql`SELECT * FROM table_name WHERE id = ${id}`","sourceCodeStart":497,"sourceCodeEnd":533,"githubUrl":"https://github.com/typeorm/typeorm/blob/04ff4daedcf60fa4ffd0d5d33bbafaac1a9bbc96/src/data-source/DataSource.ts#L497-L533","documentation":"Thrown by DataSource.query() when the supplied queryRunner has `isReleased === true`. Query runners are single-use connection handles; once release() is called the underlying connection is returned to the pool and must not be used. TypeORM refuses to route a query through a released runner.","triggerScenarios":"Passing a queryRunner to `dataSource.query(sql, params, qr)` after `await qr.release()` was already called; reusing a runner stored from a previous transaction; calling query inside a finally block after release() ran.","commonSituations":"Manual transaction code that releases the runner then continues querying; helper functions that close the runner before the caller finishes; double-release where the second use fails.","solutions":["Do not call queryRunner.release() until ALL queries through it have finished.","Create a fresh queryRunner for each unit of work instead of reusing a released one.","Restructure so release() runs in a finally AFTER the last query, not before."],"exampleFix":"// before\nconst qr = dataSource.createQueryRunner()\nawait qr.release()\nawait dataSource.query('SELECT 1', [], qr) // throws\n\n// after\nconst qr = dataSource.createQueryRunner()\ntry {\n  await dataSource.query('SELECT 1', [], qr)\n} finally {\n  await qr.release()\n}","handlingStrategy":"validation","validationCode":"if (queryRunner.isReleased) {\n  throw new Error('Query runner already released; create a new one.')\n}\nawait dataSource.query(sql, [], queryRunner)","typeGuard":"function isUsableQueryRunner(qr: QueryRunner): boolean {\n  return !qr.isReleased\n}","tryCatchPattern":"try {\n  await dataSource.query(sql, [], queryRunner)\n} catch (e) {\n  if (e instanceof QueryRunnerProviderAlreadyReleasedError) { const qr = dataSource.createQueryRunner(); /* retry on fresh runner */ }\n  else throw e\n}","preventionTips":["Always release() in a finally AFTER the last query, never before.","Never reuse a stored queryRunner across units of work.","Create a fresh queryRunner per transaction/unit of work."],"tags":["query-runner","connection-pool","lifecycle","transactions"],"analyzedSha":"04ff4daedcf60fa4ffd0d5d33bbafaac1a9bbc96","analyzedAt":"2026-08-03T18:27:32.281Z","schemaVersion":2}