remix-run/remix · error · Error

upsert requires at least one value

Error message

upsert requires at least one value

What it means

compileUpsertOperation requires at least one column to insert; if the values record is empty there is nothing to upsert and the SQL would be invalid, so the compiler throws immediately.

Source

Thrown at packages/data-table-postgres/src/lib/sql-compiler.ts:212

  return {
    text:
      'insert into ' +
      quotePath(getTableName(table)) +
      ' (' +
      quotedColumns.join(', ') +
      ') values ' +
      valueSets.join(', ') +
      compileReturningClause(returning),
    values: context.values,
  }
}

function compileUpsertOperation(operation: UpsertOperation, context: CompileContext): SqlStatement {
  let insertColumns = Object.keys(operation.values)
  let conflictTarget = operation.conflictTarget ?? [...getTablePrimaryKey(operation.table)]

  if (insertColumns.length === 0) {
    throw new Error('upsert requires at least one value')
  }

  let quotedInsertColumns = insertColumns.map((column) => quotePath(column))
  let insertPlaceholders = insertColumns.map((column) =>
    pushValue(context, operation.values[column]),
  )

  let updateValues = operation.update ?? operation.values
  let updateColumns = Object.keys(updateValues)
  let onConflictClause = ''

  if (updateColumns.length === 0) {
    onConflictClause =
      ' on conflict (' +
      conflictTarget.map((column: string) => quotePath(column)).join(', ') +
      ') do nothing'
  } else {
    onConflictClause =

View on GitHub (pinned to 9696913134)

Solutions

  1. Skip the upsert call when the values object has no keys
  2. Default to required fields or validate that the payload produced at least one column before calling upsert
  3. Fix upstream logic so empty payloads don't reach the database layer

Example fix

// before
await table.upsert({ ...maybeChanges }) // throws when empty

// after
if (Object.keys(changes).length > 0) {
  await table.upsert(changes)
}
Defensive patterns

Strategy: validation

Validate before calling

if (Object.keys(values).length === 0) { /* skip or throw a clearer error */ return }

Type guard

function hasValues(v: Record<string, unknown>): boolean { return Object.keys(v).length > 0 }

Prevention

When it happens

Trigger: Calling upsert with `{}` (empty object) as values — commonly from spreading an optional update payload that is undefined, or building values conditionally so no keys are ever set; mapping over an empty batch item.

Common situations: `...changes` where changes is empty; form submissions with no changed fields that still hit the upsert path; batch loops that generate empty rows.

Related errors


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/211a0875d9b6409f. Report an issue: GitHub.