cube-js/cube · error

Create table failed: ${e}

Error message

Create table failed: ${e}

What it means

createTable() executes the generated CREATE TABLE statement via command() and wraps any failure in this generic error, losing the original cause via string interpolation. The failure is almost always a SQL-level problem with the DDL ClickHouse rejected.

Source

Thrown at packages/cubejs-clickhouse-driver/src/ClickHouseDriver.ts:599

    return columns.map((column) => ({
      name: column.name,
      type: this.toGenericType(column.type),
    }));
  }

  // This is only for use in tests
  public override async createTableRaw(query: string): Promise<void> {
    await this.command(query);
  }

  public override async createTable(quotedTableName: string, columns: TableColumn[]) {
    const createTableSql = this.createTableSql(quotedTableName, columns);
    try {
      await this.command(createTableSql);
    } catch (e) {
      // TODO replace string formatting with proper cause
      throw new Error(`Create table failed: ${e}`);
    }
  }

  /**
   * We use unloadWithoutTempTable strategy
   */
  public async unload(_tableName: string, options: UnloadOptions): Promise<DownloadTableCSVData> {
    if (!options.query?.sql) {
      throw new Error('Query must be defined in options');
    }

    return this.unloadFromQuery(
      options.query?.sql,
      options.query?.params,
      options
    );
  }

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Read the interpolated `${e}` text for the exact ClickHouse SQL error (e.g. 'Table already exists' vs 'Access denied')
  2. Check for a leftover pre-aggregation table with the same name and drop it or let Cube rename/rebuild
  3. Grant CREATE/DDL privileges on the target database to the ClickHouse user
  4. Inspect the generated SQL (createTableSql) for unsupported column types and adjust data types in the data model
  5. Upgrade driver/ClickHouse if the DDL uses syntax unsupported by your server version

Example fix

// before
throw new Error(`Create table failed: ${e}`);
// after (in driver)
throw new Error(`Create table failed: ${e}`, { cause: e });
Defensive patterns

Strategy: try-catch

Validate before calling

// Confirm DDL rights before a load run
await driver.query('SHOW GRANTS FOR CURRENT_USER', []);

Type guard

null

Try / catch

try { await driver.createTable(name, columns); }
catch (e) {
  const cause = String(e.message);
  if (cause.includes('ALREADY_EXISTS') || cause.includes('already exists')) { /* drop or reuse table */ }
  if (cause.includes('ACCESS_DENIED')) throw new Error('Grant CREATE TABLE privilege to the Cube user', { cause: e });
  throw e;
}

Prevention

When it happens

Trigger: Driver.createTable(quotedTableName, columns) called during pre-aggregation load; command(createTableSql) rejects — table already exists, invalid engine options, bad column type mapping, or insufficient permissions.

Common situations: Resuming a failed load where the pre-aggregation table already exists; ClickHouse user lacking CREATE TABLE privilege on the target database; unsupported column types produced by toGenericType mapping; syntax differences across ClickHouse versions.

Related errors


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