drizzle-team/drizzle-orm · error · Error

values() must be called with at least one value

Error message

values() must be called with at least one value

What it means

PgInsertBuilder.values (insert.ts:84) normalizes its argument to an array and throws if that array is empty (insert.ts:88). Drizzle cannot generate an INSERT statement without at least one row, so an empty batch is treated as a programmer error rather than emitted as no-op SQL.

Source

Thrown at drizzle-orm/src/pg-core/query-builders/insert.ts:89

	/** @internal */
	setToken(token?: NeonAuthToken) {
		this.authToken = token;
		return this;
	}

	overridingSystemValue(): Omit<PgInsertBuilder<TTable, TQueryResult, true>, 'overridingSystemValue'> {
		this.overridingSystemValue_ = true;
		return this as any;
	}

	values(value: PgInsertValue<TTable, OverrideT>): PgInsertBase<TTable, TQueryResult>;
	values(values: PgInsertValue<TTable, OverrideT>[]): PgInsertBase<TTable, TQueryResult>;
	values(
		values: PgInsertValue<TTable, OverrideT> | PgInsertValue<TTable, OverrideT>[],
	): PgInsertBase<TTable, TQueryResult> {
		values = Array.isArray(values) ? values : [values];
		if (values.length === 0) {
			throw new Error('values() must be called with at least one value');
		}
		const mappedValues = values.map((entry) => {
			const result: Record<string, Param | SQL> = {};
			const cols = this.table[Table.Symbol.Columns];
			for (const colKey of Object.keys(entry)) {
				const colValue = entry[colKey as keyof typeof entry];
				result[colKey] = is(colValue, SQL) ? colValue : new Param(colValue, cols[colKey]);
			}
			return result;
		});

		return new PgInsertBase(
			this.table,
			mappedValues,
			this.session,
			this.dialect,
			this.withList,
			false,

View on GitHub (pinned to b7862528fd)

Solutions

  1. Guard the call: only invoke .values() when the row array has length > 0.
  2. If empty input is legitimate, treat it as a no-op by skipping the query entirely.
  3. Validate upstream payload size before constructing the insert.

Example fix

// before
await db.insert(users).values(rows.filter(isValid)); // crashes when empty

// after
const valid = rows.filter(isValid);
if (valid.length) await db.insert(users).values(valid);
Defensive patterns

Strategy: validation

Validate before calling

const rows = payload.filter(isValid);
if (rows.length === 0) {
  // nothing to insert; skip instead of crashing
  return { inserted: 0 };
}
await db.insert(users).values(rows);

Type guard

function isNonEmpty<T>(arr: T[]): arr is [T, ...T[]] {
  return arr.length > 0;
}

Prevention

When it happens

Trigger: Calling db.insert(t).values([]) directly, or passing a runtime-filtered array of rows that happens to be empty: db.insert(t).values(rows.filter(isValid)) when no rows pass the filter.

Common situations: Bulk-inserting from a CSV/payload that yielded zero valid rows; processing a webhook batch that is occasionally empty; refactoring to deduplicate rows and accidentally producing [].

Related errors


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