jackc/pgx · error

too many column format codes

Error message

too many column format codes

What it means

Returned by CopyOutResponse.Encode when ColumnFormatCodes has more than 65535 entries. The column count is a uint16 on the wire, so the library rejects oversized slices rather than truncating. CopyOutResponse is the 'H' message the server sends to begin a COPY TO STDOUT (server→client export) operation.

Source

Thrown at pgproto3/copy_out_response.go:53

	columnFormatCodes := make([]uint16, columnCount)
	for i := range columnCount {
		columnFormatCodes[i] = binary.BigEndian.Uint16(buf.Next(2))
	}

	*dst = CopyOutResponse{OverallFormat: overallFormat, ColumnFormatCodes: columnFormatCodes}

	return nil
}

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

	dst = append(dst, src.OverallFormat)

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

	return finishMessage(dst, sp)
}

// MarshalJSON implements encoding/json.Marshaler.
func (src CopyOutResponse) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type              string
		ColumnFormatCodes []uint16
	}{
		Type:              "CopyOutResponse",
		ColumnFormatCodes: src.ColumnFormatCodes,
	})

View on GitHub (pinned to ec1a0befd2)

Solutions

  1. Cap ColumnFormatCodes at 65535; a one-element slice applies the format to all columns.
  2. Inspect the construction code for an unbounded or per-row append loop.
  3. When proxying, pass through the server's raw encoded bytes instead of re-encoding.
  4. Confirm the column count value is not a corrupted upstream read.

Example fix

// before
resp := &pgproto3.CopyOutResponse{
    OverallFormat:     0,
    ColumnFormatCodes: hugeCodes, // > 65535
}
_, err := resp.Encode(nil)

// after
resp := &pgproto3.CopyOutResponse{
    OverallFormat:     0,
    ColumnFormatCodes: []uint16{0},
}
_, err := resp.Encode(nil)
Defensive patterns

Strategy: validation

Validate before calling

func validateCopyOutResponseEncode(r *pgproto3.CopyOutResponse) error {
	if len(r.ColumnFormatCodes) > math.MaxUint16 {
		return fmt.Errorf("too many column format codes: %d (max %d)", len(r.ColumnFormatCodes), math.MaxUint16)
	}
	return nil
}

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Calling `(*CopyOutResponse).Encode(dst)` with `len(ColumnFormatCodes) > 65535`. Like the other Copy responses, this is server-originated; the error only fires in a proxy/server/test harness, not in a standard pgx client.

Common situations: A proxy or fuzzer constructs a CopyOutResponse for an artificial table with >64k columns, or a mis-scoped loop appends format codes beyond the column count.

Related errors


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