{"id":"bf431938adc6aa11","repo":"drizzle-team/drizzle-orm","slug":"values-must-be-called-with-at-least-one-value-bf4319","errorCode":null,"errorMessage":"values() must be called with at least one value","messagePattern":"values\\(\\) must be called with at least one value","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"drizzle-orm/src/pg-core/query-builders/insert.ts","lineNumber":89,"sourceCode":"\t/** @internal */\n\tsetToken(token?: NeonAuthToken) {\n\t\tthis.authToken = token;\n\t\treturn this;\n\t}\n\n\toverridingSystemValue(): Omit<PgInsertBuilder<TTable, TQueryResult, true>, 'overridingSystemValue'> {\n\t\tthis.overridingSystemValue_ = true;\n\t\treturn this as any;\n\t}\n\n\tvalues(value: PgInsertValue<TTable, OverrideT>): PgInsertBase<TTable, TQueryResult>;\n\tvalues(values: PgInsertValue<TTable, OverrideT>[]): PgInsertBase<TTable, TQueryResult>;\n\tvalues(\n\t\tvalues: PgInsertValue<TTable, OverrideT> | PgInsertValue<TTable, OverrideT>[],\n\t): PgInsertBase<TTable, TQueryResult> {\n\t\tvalues = Array.isArray(values) ? values : [values];\n\t\tif (values.length === 0) {\n\t\t\tthrow new Error('values() must be called with at least one value');\n\t\t}\n\t\tconst mappedValues = values.map((entry) => {\n\t\t\tconst result: Record<string, Param | SQL> = {};\n\t\t\tconst cols = this.table[Table.Symbol.Columns];\n\t\t\tfor (const colKey of Object.keys(entry)) {\n\t\t\t\tconst colValue = entry[colKey as keyof typeof entry];\n\t\t\t\tresult[colKey] = is(colValue, SQL) ? colValue : new Param(colValue, cols[colKey]);\n\t\t\t}\n\t\t\treturn result;\n\t\t});\n\n\t\treturn new PgInsertBase(\n\t\t\tthis.table,\n\t\t\tmappedValues,\n\t\t\tthis.session,\n\t\t\tthis.dialect,\n\t\t\tthis.withList,\n\t\t\tfalse,","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/drizzle-team/drizzle-orm/blob/b7862528fd8fc39bc2653a6c18dad7c1f4e68d10/drizzle-orm/src/pg-core/query-builders/insert.ts#L71-L107","documentation":"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.","triggerScenarios":"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.","commonSituations":"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 [].","solutions":["Guard the call: only invoke .values() when the row array has length > 0.","If empty input is legitimate, treat it as a no-op by skipping the query entirely.","Validate upstream payload size before constructing the insert."],"exampleFix":"// before\nawait db.insert(users).values(rows.filter(isValid)); // crashes when empty\n\n// after\nconst valid = rows.filter(isValid);\nif (valid.length) await db.insert(users).values(valid);","handlingStrategy":"validation","validationCode":"const rows = payload.filter(isValid);\nif (rows.length === 0) {\n  // nothing to insert; skip instead of crashing\n  return { inserted: 0 };\n}\nawait db.insert(users).values(rows);","typeGuard":"function isNonEmpty<T>(arr: T[]): arr is [T, ...T[]] {\n  return arr.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Guard bulk inserts with an array-length check.","Treat empty input as a legitimate no-op in batch loaders.","Validate payload size at the API boundary."],"tags":["insert","bulk","validation"],"analyzedSha":"b7862528fd8fc39bc2653a6c18dad7c1f4e68d10","analyzedAt":"2026-08-03T18:11:14.318Z","schemaVersion":2}