drizzle-team/drizzle-orm · error · Error

Cannot execute a query on a query builder. Please use a data

Error message

Cannot execute a query on a query builder. Please use a database instance instead.

What it means

Thrown by MySqlSelectBase.prepare (line 1130) when this.session is undefined. Select builders created from a QueryBuilder instance (new QueryBuilder() or the $db builder) have no session and therefore cannot execute. Only a db instance (which carries a session) can run queries.

Source

Thrown at drizzle-orm/src/mysql-core/query-builders/select.ts:1130

	TResult = SelectResult<TSelection, TSelectMode, TNullabilityMap>[],
	TSelectedFields = BuildSubquerySelection<TSelection, TNullabilityMap>,
> extends MySqlSelectQueryBuilderBase<
	MySqlSelectHKT,
	TTableName,
	TSelection,
	TSelectMode,
	TPreparedQueryHKT,
	TNullabilityMap,
	TDynamic,
	TExcludedMethods,
	TResult,
	TSelectedFields
> {
	static override readonly [entityKind]: string = 'MySqlSelect';

	prepare(): MySqlSelectPrepare<this> {
		if (!this.session) {
			throw new Error('Cannot execute a query on a query builder. Please use a database instance instead.');
		}
		const fieldsList = orderSelectedFields<MySqlColumn>(this.config.fields);
		const query = this.session.prepareQuery<
			MySqlPreparedQueryConfig & { execute: SelectResult<TSelection, TSelectMode, TNullabilityMap>[] },
			TPreparedQueryHKT
		>(this.dialect.sqlToQuery(this.getSQL()), fieldsList, undefined, undefined, undefined, {
			type: 'select',
			tables: [...this.usedTables],
		}, this.cacheConfig);
		query.joinsNotNullableMap = this.joinsNotNullableMap;
		return query as MySqlSelectPrepare<this>;
	}

	execute = ((placeholderValues) => {
		return this.prepare().execute(placeholderValues);
	}) as ReturnType<this['prepare']>['execute'];

	private createIterator = (): ReturnType<this['prepare']>['iterator'] => {

View on GitHub (pinned to b7862528fd)

Solutions

  1. Execute selects from a db instance: const db = drizzle(...); db.select().from(t).execute();
  2. If you built a query with QueryBuilder for reuse, pass it to a db via db.$withRecursive or run its SQL with db.execute(sql`...`).
  3. Make sure you're not accidentally destructuring select off a QueryBuilder instead of db.

Example fix

// before - QueryBuilder has no session
import { QueryBuilder } from 'drizzle-orm/mysql-core';
const qb = new QueryBuilder();
await qb.select().from(users).execute(); // throws

// after - use a db instance
import { drizzle } from 'drizzle-orm/mysql2';
const db = drizzle(client);
await db.select().from(users).execute();
Defensive patterns

Strategy: type-guard

Validate before calling

// Ensure you execute from a db, not a QueryBuilder
if (!('session' in dbOrBuilder) || !(dbOrBuilder as any).session) {
  throw new Error('Use a drizzle() db instance to execute queries');
}

Type guard

import { MySqlDatabase } from '~/mysql-core/db.ts';
function isExecutableDb(v: unknown): v is MySqlDatabase<any, any, any, any> {
  return v instanceof MySqlDatabase;
}

Prevention

When it happens

Trigger: Constructing a select via a QueryBuilder (not a db) and then calling .execute(), .prepare(), .all(), or iterating. The QueryBuilder is meant for composing SQL to be run elsewhere, not for direct execution.

Common situations: Using `import { QueryBuilder } from 'drizzle-orm/mysql-core'; const qb = new QueryBuilder(); qb.select().from(t).execute();` - qb has no session. Confusing QueryBuilder with the db instance.

Related errors


AI-assisted analysis of drizzle-team/drizzle-orm@b7862528fd (2026-08-03). Data as JSON: /data/errors/e4ea7b1253581574.json. Report an issue: GitHub.