cube-js/cube · error · Error

${this.constructor} driver supports only rows upload

Error message

${this.constructor} driver supports only rows upload

What it means

uploadTableWithIndexes() only supports table data supplied as in-memory rows (isDownloadTableMemoryData). If tableData is a stream-based or reference-based (e.g. unload-to-S3) form, the driver throws this error.

Source

Thrown at packages/cubejs-base-driver/src/BaseDriver.ts:489

  public param(_paramIndex: number): string {
    return '?';
  }

  public testConnectionTimeout() {
    return this.testConnectionTimeoutValue;
  }

  public async downloadTable(table: string, _options: ExternalDriverCompatibilities): Promise<TableMemoryData> {
    return { rows: await this.query(`SELECT * FROM ${table}`) };
  }

  public async uploadTable(table: string, columns: TableStructure, tableData: DownloadTableData) {
    return this.uploadTableWithIndexes(table, columns, tableData, [], null, [], {});
  }

  public async uploadTableWithIndexes(table: string, columns: TableStructure, tableData: DownloadTableData, indexesSql: IndexesSQL, _uniqueKeyColumns: string[] | null, _queryTracingObj: any, _externalOptions: ExternalCreateTableOptions) {
    if (!isDownloadTableMemoryData(tableData)) {
      throw new Error(`${this.constructor} driver supports only rows upload`);
    }

    await this.createTable(table, columns);
    try {
      if (isDownloadTableMemoryData(tableData)) {
        for (let i = 0; i < tableData.rows.length; i++) {
          await this.query(
            `INSERT INTO ${table}
          (${columns.map(c => this.quoteIdentifier(c.name)).join(', ')})
          VALUES (${columns.map((c, paramIndex) => this.param(paramIndex)).join(', ')})`,
            columns.map(c => this.toColumnValue(tableData.rows[i][c.name] as string, c.type))
          );
        }
        for (let i = 0; i < indexesSql.length; i++) {
          const [query, params] = indexesSql[i].sql;
          await this.query(query, params);
        }
      }

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Materialize the data into rows before uploading (fetch stream fully and pass { rows })
  2. Use a driver/storage-fs path that supports the given data form (e.g. CSV unload/ingest instead of row upload)
  3. Convert remote CSV files to rows via downloadQueryResults or CSV parsing
  4. Check which DownloadTableData variant the target driver supports

Example fix

// before
target.uploadTable('tbl', cols, unloadedCsvRef); // stream/ref form
// after
const rows = await parseCsvToRows(unloadedCsvRef);
target.uploadTable('tbl', cols, { rows });
Defensive patterns

Strategy: type-guard

Validate before calling

import { isDownloadTableMemoryData } from '@cubejs-backend/base-driver';
if (!isDownloadTableMemoryData(tableData)) throw new Error('Driver needs rows: materialize before upload');

Type guard

function isRowUploadData(d: DownloadTableData): d is DownloadTableMemoryData {
  return Array.isArray((d as any).rows);
}

Try / catch

try {
  await target.uploadTable(table, cols, data);
} catch (e) {
  if (/supports only rows upload/.test(e.message)) {
    const rows = await materializeToRows(data);
    await target.uploadTable(table, cols, { rows });
  } else throw e;
}

Prevention

When it happens

Trigger: Calling uploadTable()/uploadTableWithIndexes() with DownloadTableData that contains streams or remote file references instead of a rows array.

Common situations: Copying unload results (e.g. from BigQuery unload producing CSV URLs) into a driver that only accepts row arrays, or passing streamed query results between drivers.

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/f055a6189cf587b9. Report an issue: GitHub.