{"record":{"id":"d0748a6b05e07955","repo":"remix-run/remix","slug":"insertmany-requires-at-least-one-explicit-value","errorCode":null,"errorMessage":"insertMany() requires at least one explicit value across the batch","messagePattern":"insertMany\\(\\) requires at least one explicit value across the batch","errorType":"exception","errorClass":"DataTableQueryError","httpStatus":null,"severity":"error","filePath":"packages/data-table/src/lib/database/query-execution.ts","lineNumber":315,"sourceCode":"    insertId: result.insertId,\n  }\n}\n\nasync function executeInsertMany(\n  database: QueryExecutionContext,\n  table: AnyTable,\n  values: Record<string, unknown>[],\n  options?: { returning?: ReturningInput<Record<string, unknown>>; touch?: boolean },\n): Promise<WriteResult | WriteRowsResult<Record<string, unknown>>> {\n  let preparedValues = values.map((value) =>\n    prepareInsertValues(table, value as never, database.now(), options?.touch ?? true),\n  )\n\n  if (\n    preparedValues.length > 0 &&\n    preparedValues.every((preparedValue) => Object.keys(preparedValue).length === 0)\n  ) {\n    throw new DataTableQueryError(\n      'insertMany() requires at least one explicit value across the batch',\n    )\n  }\n\n  let returning = options?.returning\n  assertReturningCapability(database.capabilities, 'insertMany', returning)\n\n  if (returning) {\n    let operation: InsertManyOperation<AnyTable> = {\n      kind: 'insertMany',\n      table,\n      values: preparedValues,\n      returning: normalizeReturningSelection(returning),\n    }\n\n    let result = await database[executeOperation](operation)\n    let affectedRows = result.affectedRows ?? 0\n    runAfterWriteHook(table, {","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/data-table/src/lib/database/query-execution.ts#L297-L333","documentation":"insertMany() validates that the prepared batch contains at least one explicit column value overall. If every row object in the batch is empty ({} for each), the generated INSERT would have no columns at all, which no database can execute, so a DataTableQueryError is thrown. Note the check is across the batch: an individual empty row is tolerated as long as some other row has values.","triggerScenarios":"await table.insertMany({ values: [{}, {}] }), or values mapped from an array where every element failed to pick up any fields (e.g. destructuring bug or all fields undefined-stripped).","commonSituations":"Building the batch from request bodies or CSV rows where the field mapping dropped every key (wrong casing, empty file, filter removing all properties); accidentally passing an array of empty objects after normalizing undefined values away.","solutions":["Check the source data: ensure at least one row in the batch has at least one column value","Guard before calling: if (rows.every(r => Object.keys(r).length === 0)) skip or reject the request","Fix the upstream mapping/normalization that is stripping all fields from each row"],"exampleFix":"// before\nawait table.insertMany({ values: rows.map(() => ({})) })\n\n// after\nlet batch = rows.map(r => ({ name: r.name, email: r.email }))\nif (batch.some(r => Object.keys(r).length > 0)) {\n  await table.insertMany({ values: batch })\n}","handlingStrategy":"validation","validationCode":"let hasValues = batch.some((row) => Object.keys(row).length > 0)\nif (!hasValues) throw new Error('Nothing to insert')","typeGuard":"function isNonEmptyInsertBatch(batch: Record<string, unknown>[]): boolean {\n  return batch.some((row) => Object.keys(row).length > 0)\n}","tryCatchPattern":"try {\n  await table.insertMany({ values: batch })\n} catch (error) {\n  if (error instanceof DataTableQueryError && /at least one explicit value/.test(error.message)) {\n    return { inserted: 0 } // no-op batch\n  } else throw error\n}","preventionTips":["Validate upstream data mapping before batching (assert at least one field per source row survives)","Reject empty CSV/form uploads before reaching the database layer","Log row-shape statistics when building batches to catch mapping regressions"],"tags":["data-table","insert-many","validation","empty-values"],"backgroundTag":"empty-insert-batch","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}