jackc/pgx · error

too many column format codes

Error message

too many column format codes

What it means

Returned by CopyInResponse.Encode when ColumnFormatCodes has more than 65535 entries. The column count is encoded as a uint16, so values beyond MaxUint16 cannot fit. The guard avoids silently truncating the count. CopyInResponse is the 'G' message the server sends to begin a COPY FROM STDIN (client→server bulk load) operation.

Source

Thrown at pgproto3/copy_in_response.go:53

	}

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

	*dst = CopyInResponse{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 *CopyInResponse) Encode(dst []byte) ([]byte, error) {
	dst, sp := beginMessage(dst, 'G')

	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 CopyInResponse) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type              string
		ColumnFormatCodes []uint16
	}{
		Type:              "CopyInResponse",
		ColumnFormatCodes: src.ColumnFormatCodes,
	})

View on GitHub (pinned to ec1a0befd2)

Solutions

  1. Cap ColumnFormatCodes at 65535; a single-entry slice is valid and applies the format to all columns.
  2. Audit the construction loop for an unbounded or mis-scoped append.
  3. If proxying, forward the server's raw encoded bytes instead of decoding and re-encoding.
  4. Verify the column count source is not a corrupted value read from elsewhere.

Example fix

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

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

Strategy: validation

Validate before calling

func validateCopyInResponseEncode(r *pgproto3.CopyInResponse) 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 `(*CopyInResponse).Encode(dst)` with `len(ColumnFormatCodes) > 65535`. CopyInResponse is a server-originated message, so this only fires in a proxy/server/test harness synthesising one, never in a normal pgx client.

Common situations: A PostgreSQL-compatible proxy or test server builds a CopyInResponse with a per-column format slice for an artificially wide table (>64k columns). A loop that appends format codes per-row instead of per-column can also blow the limit.

Related errors


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