jackc/pgx · error

too many parameters

Error message

too many parameters

What it means

Returned by Bind.Encode in pgproto3/bind.go:130 when len(src.Parameters) exceeds 65535 (math.MaxUint16). The wire protocol encodes the parameter count as a uint16, so a single Bind cannot carry more than 65535 parameters. Fired client-side during Encode, before the message is sent.

Source

Thrown at pgproto3/bind.go:130

// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
func (src *Bind) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'B')

	dst = append(dst, src.DestinationPortal...)
	dst = append(dst, 0)
	dst = append(dst, src.PreparedStatement...)
	dst = append(dst, 0)

	if len(src.ParameterFormatCodes) > math.MaxUint16 {
		return nil, errors.New("too many parameter format codes")
	}
	dst = pgio.AppendUint16(dst, uint16(len(src.ParameterFormatCodes)))
	for _, fc := range src.ParameterFormatCodes {
		dst = pgio.AppendInt16(dst, fc)
	}

	if len(src.Parameters) > math.MaxUint16 {
		return nil, errors.New("too many parameters")
	}
	dst = pgio.AppendUint16(dst, uint16(len(src.Parameters)))
	for _, p := range src.Parameters {
		if p == nil {
			dst = pgio.AppendInt32(dst, -1)
			continue
		}

		dst = pgio.AppendInt32(dst, int32(len(p)))
		dst = append(dst, p...)
	}

	if len(src.ResultFormatCodes) > math.MaxUint16 {
		return nil, errors.New("too many result format codes")
	}
	dst = pgio.AppendUint16(dst, uint16(len(src.ResultFormatCodes)))
	for _, fc := range src.ResultFormatCodes {
		dst = pgio.AppendInt16(dst, fc)

View on GitHub (pinned to ec1a0befd2)

Solutions

  1. Split the statement so each execution binds <=65535 parameters.
  2. Use pgx CopyFrom / PostgreSQL COPY for bulk inserts.
  3. For large IN-lists, pass values via a temp table, array parameter (ANY(unnest($1::int[]))), or jsonb instead of thousands of placeholders.
  4. Add a unit test asserting placeholder count stays under the limit.

Example fix

// before: 100000 placeholders in one statement
placeholders := make([]string, 100000)
for i := range placeholders { placeholders[i] = fmt.Sprintf("$%d", i+1) }
q := "SELECT * FROM t WHERE id IN (" + strings.Join(placeholders, ",") + ")"
rows, _ := conn.Exec(ctx, q, args...) // -> "too many parameters"

// after: use an array parameter to avoid the placeholder explosion
rows, _ := conn.Query(ctx, "SELECT * FROM t WHERE id = ANY($1::int[])", ids)
Defensive patterns

Strategy: validation

Validate before calling

const maxBindParams = 65535 // math.MaxUint16
if len(args) > maxBindParams {
    return fmt.Errorf("query has %d bind parameters, protocol max is %d; split the batch or use COPY/ANY($1::array)", len(args), maxBindParams)
}

Type guard

func bindParamCountOK(params [][]byte) bool { return len(params) <= 65535 }

Try / catch

if _, err := bindMsg.Encode(dst); err != nil {
    if strings.Contains(err.Error(), "too many parameters") {
        // re-issue in chunks of <=65535 params, or switch to COPY / array param
    }
}

Prevention

When it happens

Trigger: Executing a parameterized query or prepared statement with more than 65535 bind parameters (e.g. INSERT ... VALUES with a huge generated placeholder list, or a WHERE col IN (?, ?, ...) with too many placeholders).

Common situations: Dynamically building an IN-list or multi-row INSERT that exceeds 65535 placeholders; bulk-loading rows via parameterized INSERTs instead of COPY; concatenating user input into placeholder lists without a limit.

Related errors


AI-assisted analysis of jackc/pgx@ec1a0befd2 (2026-08-04). Data as JSON: /data/errors/de441029213186d3.json. Report an issue: GitHub.