remix-run/remix · error · DataTableQueryError

' + operation + '() returning is not supported by this datab

Error message

' + operation + '() returning is not supported by this database

What it means

All mutating operations accept a returning option to fetch written rows back, but the adapter's capabilities.returning is false for this database (e.g. MySQL). If returning is requested in that case, a DataTableQueryError is thrown naming the operation, because the library cannot emulate row returning there.

Source

Thrown at packages/data-table/src/lib/database/write-lifecycle.ts:257

  context: TableAfterDeleteContext,
): void {
  let callback = getTableAfterDelete(table)

  if (!callback) {
    return
  }

  let callbackResult = callback(context)
  assertSynchronousCallbackResult(context.tableName, 'delete', 'afterDelete', callbackResult)
}

export function assertReturningCapability<row extends Record<string, unknown>>(
  capabilities: DatabaseCapabilities,
  operation: 'insert' | 'insertMany' | 'update' | 'delete' | 'upsert',
  returning: ReturningInput<row> | undefined,
): void {
  if (returning && !capabilities.returning) {
    throw new DataTableQueryError(operation + '() returning is not supported by this database')
  }
}

export function normalizeReturningSelection<row extends Record<string, unknown>>(
  returning: ReturningInput<row>,
): ReturningSelection {
  if (returning === '*') {
    return '*'
  }

  return [...returning]
}

function validateWriteValues<table extends AnyTable>(
  table: table,
  values: Partial<TableRow<table>>,
  operation: TableWriteOperation,
): Record<string, unknown> {

View on GitHub (pinned to 9696913134)

Solutions

  1. Remove the returning option and re-query rows afterwards with a select where clause
  2. Use a database/driver that supports RETURNING (Postgres, recent SQLite)
  3. Branch on database.capabilities.returning if you must support both

Example fix

// before
await table.insert({ values, returning: ['id', 'createdAt'] })

// after
let { id } = await table.insert({ values })
let row = await table.row({ where: { id } })
Defensive patterns

Strategy: type-guard

Validate before calling

let canReturn = database.capabilities.returning
await table.insert({ values, ...(canReturn ? { returning: ['id'] } : {}) })

Type guard

function supportsReturning(database: { capabilities: { returning: boolean } }): boolean {
  return database.capabilities.returning === true
}

Try / catch

try {
  result = await table.insert({ values, returning: ['id', 'createdAt'] })
} catch (error) {
  if (error instanceof DataTableQueryError && /returning is not supported/.test(error.message)) {
    result = await table.insert({ values })
    row = await table.row({ where: { id: result.id } })
  } else throw error
}

Prevention

When it happens

Trigger: insert/insertMany/update/delete/upsert called with returning: [...] or returning: true while database.capabilities.returning is false.

Common situations: Writing code against Postgres locally then running tests or production on MySQL; upgrading an adapter whose capability detection changed; assuming returning works everywhere because the type accepts it.

Related errors


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