remix-run/remix · error · DataTableDatabaseError

Database execution failed

Error message

Database execution failed

What it means

This is the generic wrapper the Database puts around every driver.execute failure. The original error is preserved as cause, and metadata records the dialect and operation.kind, so you can inspect what dialect and what kind of operation (select/insert/update/...) failed. The underlying reason is almost always a SQL error: constraint violation, missing table/column, type mismatch, or syntax the driver rejected.

Source

Thrown at packages/data-table/src/lib/database.ts:970

      }
      if (failures.length > 1) {
        throw new AggregateError(failures, 'Nested transaction cleanup failed', { cause: error })
      }
      throw error
    }

    await this.#driver.releaseSavepoint(this.#token, savepointName)
    return result
  }

  async #executeOperation(operation: DataManipulationOperation): Promise<DataManipulationResult> {
    try {
      return await this.#driver.execute({
        operation,
        transaction: this.#token,
      })
    } catch (error) {
      throw new DataTableDatabaseError('Database execution failed', {
        cause: error,
        metadata: {
          dialect: this.dialect,
          operationKind: operation.kind,
        },
      })
    }
  }
}

function defaultNow(): Date {
  return new Date()
}

View on GitHub (pinned to 9696913134)

Solutions

  1. Inspect error.cause for the driver's real message (e.g. UNIQUE constraint failed) and fix the data or schema accordingly.
  2. Run migrations / verify the schema (db.migrationStatus()) if tables or columns are missing.
  3. For SQLite lock errors, serialize writes through a single connection/queue.

Example fix

// before
await db.create(users, values)

// after
try {
  await db.create(users, values)
} catch (error) {
  console.error(error.cause?.message, error.metadata)
  throw error
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Catch at the boundary and surface the real cause
try {
  await db.execute(operation)
} catch (error) {
  throw new Response(error.cause?.message ?? 'Database error', { status: 500 })
}

Type guard

function isDatabaseExecutionError(error: unknown): error is DataTableDatabaseError {
  return error instanceof DataTableDatabaseError && error.message === 'Database execution failed'
}

Try / catch

try {
  await db.create(users, values)
} catch (error) {
  if (isDatabaseExecutionError(error)) {
    console.error(error.cause, error.metadata) // dialect + operationKind
  }
  throw error
}

Prevention

When it happens

Trigger: Any query/insert/update/delete whose compiled SQL fails: UNIQUE/FK/NOT NULL constraint violations, no such table (migrations not run), no such column (schema drift), locked database, or invalid values.

Common situations: Running against a database missing migrations; schema changes not applied in an environment; constraint violations on inserts; SQLite 'database is locked' under concurrent writers.

Related errors


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