cube-js/cube · error · PostgresError

PostgreSQL protocol does not support more than 65535 paramet

Error message

PostgreSQL protocol does not support more than 65535 parameters, but ${length} passed

What it means

The PostgreSQL wire protocol caps bind-message parameters at 65535 (the count field is a 16-bit integer). The pg module does not validate this and would send a corrupted bind message, so PostgresDriver.checkValuesLimit proactively throws when the parameter array length reaches 65536 or more.

Source

Thrown at packages/cubejs-postgres-driver/src/PostgresDriver.ts:413

        }
      };
    } catch (e) {
      await this.pool.release(conn);

      throw e;
    }
  }

  protected static checkValuesLimit(values?: unknown[]) {
    // PostgreSQL protocol allows sending up to 65535 params in a single bind message
    // See https://github.com/postgres/postgres/blob/REL_16_0/src/backend/tcop/postgres.c#L1698-L1708
    // See https://github.com/postgres/postgres/blob/REL_16_0/src/backend/libpq/pqformat.c#L428-L431
    // But 'pg' module does not check for params count, and ends up sending incorrect bind message
    // See https://github.com/brianc/node-postgres/blob/92cb640fd316972e323ced6256b2acd89b1b58e0/packages/pg-protocol/src/serializer.ts#L155
    // See https://github.com/brianc/node-postgres/blob/92cb640fd316972e323ced6256b2acd89b1b58e0/packages/pg-protocol/src/buffer-writer.ts#L32-L37
    const length = (values?.length ?? 0);
    if (length >= 65536) {
      throw new PostgresError(`PostgreSQL protocol does not support more than 65535 parameters, but ${length} passed`);
    }
  }

  protected async withConnection<T>(fn: (conn: PgClient) => Promise<T>): Promise<T> {
    const conn = await this.pool.acquire();

    try {
      return await fn(conn);
    } finally {
      await this.pool.release(conn);
    }
  }

  protected async queryResponse(query: string, values: unknown[]) {
    PostgresDriver.checkValuesLimit(values);

    return this.withConnection(async (conn) => {
      await this.prepareConnection(conn);

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Split the query into batches, keeping each under 65535 parameters (e.g., chunk the IN-list or use BatchInsert via the driver's uploadTable API).
  2. Use a temporary/staging table plus JOIN instead of a huge IN-list.
  3. Reduce parameter count with range predicates (BETWEEN) or unnest/array passing where appropriate.
  4. Use the driver's built-in uploadTableWithIndexes flow which batches inserts (~1000 rows per batch in MySQL driver equivalents).

Example fix

// before
await driver.query(`SELECT * FROM t WHERE id IN (${ids.map((_, i) => `$${i + 1}`).join(',')})`, ids); // ids.length = 100000
// after
for (const chunk of _.chunk(ids, 50000)) {
  await driver.query(`SELECT * FROM t WHERE id IN (${chunk.map((_, i) => `$${i + 1}`).join(',')})`, chunk);
}
Defensive patterns

Strategy: validation

Validate before calling

function assertParamsUnderLimit(values = []) {
  if (values.length >= 65536) {
    throw new Error(`${values.length} params exceeds the 65535 PostgreSQL protocol limit; batch the query`);
  }
}

Try / catch

try {
  await driver.query(sql, values);
} catch (e) {
  if (e.message.includes('65535 parameters')) {
    console.error('Chunk the parameter list below 65536 or use a staging table');
  }
  throw e;
}

Prevention

When it happens

Trigger: Executing a parameterized query (e.g., setParameterTypes/bind path used by query/downloadQueryResults) where the values array has >= 65536 entries — typically a giant IN-list or bulk insert built by the caller.

Common situations: Uploading very large tables in one statement; generating filters with tens of thousands of values; custom code passing unbatched row data to driver queries.

Related errors


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