drizzle-team/drizzle-orm · error · DrizzleQueryError

Failed query: ${queryString} params: ${params}

Error message

Failed query: ${queryString}
params: ${params}

What it means

A DrizzleQueryError thrown from SQLiteSession.queryWithCache (session.ts:80) on the no-cache code path (cache undefined, a NoopCache, or no queryMetadata). When the underlying driver query rejects, drizzle wraps it so the thrown error carries the generated SQL and bound params. The original driver error is preserved on .cause.

Source

Thrown at drizzle-orm/src/sqlite-core/session.ts:80

		if (cache && cache.strategy() === 'all' && cacheConfig === undefined) {
			this.cacheConfig = { enable: true, autoInvalidate: true };
		}
		if (!this.cacheConfig?.enable) {
			this.cacheConfig = undefined;
		}
	}

	/** @internal */
	protected async queryWithCache<T>(
		queryString: string,
		params: any[],
		query: () => Promise<T>,
	): Promise<T> {
		if (this.cache === undefined || is(this.cache, NoopCache) || this.queryMetadata === undefined) {
			try {
				return await query();
			} catch (e) {
				throw new DrizzleQueryError(queryString, params, e as Error);
			}
		}

		// don't do any mutations, if globally is false
		if (this.cacheConfig && !this.cacheConfig.enable) {
			try {
				return await query();
			} catch (e) {
				throw new DrizzleQueryError(queryString, params, e as Error);
			}
		}

		// For mutate queries, we should query the database, wait for a response, and then perform invalidation
		if (
			(
				this.queryMetadata.type === 'insert' || this.queryMetadata.type === 'update'
				|| this.queryMetadata.type === 'delete'
			) && this.queryMetadata.tables.length > 0

View on GitHub (pinned to b7862528fd)

Solutions

  1. Inspect error.cause for the native SQLite error code (e.g. SQLITE_CONSTRAINT) to pinpoint the root cause.
  2. Read the embedded queryString/params in the DrizzleQueryError to reproduce the statement.
  3. Fix the underlying schema/data/concurrency issue (add missing table, dedupe inserts, wrap in a transaction/retry on SQLITE_BUSY).

Example fix

// before
await db.insert(users).values({ id: 1 });
// duplicate id -> DrizzleQueryError: Failed query: insert into ...

// after — handle the constraint
try {
  await db.insert(users).values({ id: 1 });
} catch (e) {
  if (e instanceof DrizzleQueryError && /UNIQUE constraint/i.test(e.cause?.message ?? '')) {
    // duplicate — ignore or upsert
  } else throw e;
}
Defensive patterns

Strategy: try-catch

Type guard

import { DrizzleQueryError } from 'drizzle-orm';
const isDrizzleQueryError = (e): e is DrizzleQueryError => e instanceof DrizzleQueryError;

Try / catch

try {
  await db.insert(users).values(row);
} catch (e) {
  if (e instanceof DrizzleQueryError) {
    // e.query, e.params, e.cause (native SQLite error)
    if (/UNIQUE constraint/i.test(e.cause?.message ?? '')) {
      // handle duplicate
    } else {
      logger.error({ sql: e.query, params: e.params, cause: e.cause?.message });
      throw e;
    }
  } else throw e;
}

Prevention

When it happens

Trigger: Any SELECT/INSERT/UPDATE/DELETE executed through a session that has no cache configured, where the database itself returns an error: constraint violation, syntax error, missing table, busy/locked, type mismatch, etc. The wrapper fires because the session has no cache to consult.

Common situations: Unique-constraint violations on insert; NOT NULL violations; querying a non-existent table after a schema change; SQLite SQLITE_BUSY under concurrency; type affinity mismatches; malformed SQL from raw sql`` fragments.

Related errors


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