remix-run/remix · error · Error
upsert requires at least one value
Error message
upsert requires at least one value
What it means
compileUpsertOperation requires the values object of an upsert operation to contain at least one column. An upsert with zero keys would generate an INSERT with no columns, which is invalid SQL, so the compiler rejects it early with a descriptive message.
Source
Thrown at packages/data-table-sqlite/src/lib/sql-compiler.ts:212
.map((column) => {
let value = Object.prototype.hasOwnProperty.call(row, column) ? row[column] : null
return pushValue(context, value)
})
.join(', ') +
')',
)
.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 updateValues = operation.update ?? operation.values
let updateColumns = Object.keys(updateValues)
let conflictClause = ''
if (updateColumns.length === 0) {
conflictClause =
' on conflict (' +
conflictTarget.map((column: string) => quotePath(column)).join(', ') +
') do nothing'
} else {
conflictClause =
' on conflict (' +
conflictTarget.map((column: string) => quotePath(column)).join(', ') +
') do update set ' +
updateColumnsView on GitHub (pinned to 9696913134)
Solutions
- Validate that the values object has at least one key before calling upsert and skip or return a 400 otherwise.
- Fix the upstream construction (spread of empty object, filtered mapping) so it never produces an empty payload.
Example fix
// before
await query.upsert(table, values /* {} */)
// after
if (Object.keys(values).length === 0) {
throw new Response('Empty payload', { status: 400 })
}
await query.upsert(table, values) Defensive patterns
Strategy: validation
Validate before calling
if (Object.keys(values).length === 0) {
throw new Response('Payload must include at least one column', { status: 400 })
}
await query.upsert(table, values) Type guard
function hasUpsertValues(values: Record<string, unknown>): boolean {
return Object.keys(values).length > 0
} Prevention
- Validate dynamic payloads before upsert.
- Unit-test the empty-payload edge case in handlers that spread optional fields.
When it happens
Trigger: Calling upsert-like APIs with values = {} or an object built from spread/loop that ends up empty, e.g. inserting {} or Object.fromEntries([]).
Common situations: Building values dynamically (e.g. sanitizing input strips every key, or mapping an empty array to an object) and passing the empty result to upsert; defaulting to {} when optional payload fields are missing.
Related errors
- upsert requires at least one value
- upsert requires at least one value
- Unsupported operation kind
- Unsupported predicate
- expected error to be ${describeExpectedError(args[0])}, got
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/78c819c22bbb583a.
Report an issue: GitHub.