{"id":"e565f321639b6cc2","repo":"jackc/pgx","slug":"too-many-column-format-codes-e565f3","errorCode":null,"errorMessage":"too many column format codes","messagePattern":"too many column format codes","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pgproto3/copy_in_response.go","lineNumber":53,"sourceCode":"\t}\n\n\tcolumnFormatCodes := make([]uint16, columnCount)\n\tfor i := range columnCount {\n\t\tcolumnFormatCodes[i] = binary.BigEndian.Uint16(buf.Next(2))\n\t}\n\n\t*dst = CopyInResponse{OverallFormat: overallFormat, ColumnFormatCodes: columnFormatCodes}\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *CopyInResponse) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'G')\n\n\tdst = append(dst, src.OverallFormat)\n\tif len(src.ColumnFormatCodes) > math.MaxUint16 {\n\t\treturn nil, errors.New(\"too many column format codes\")\n\t}\n\tdst = pgio.AppendUint16(dst, uint16(len(src.ColumnFormatCodes)))\n\tfor _, fc := range src.ColumnFormatCodes {\n\t\tdst = pgio.AppendUint16(dst, fc)\n\t}\n\n\treturn finishMessage(dst, sp)\n}\n\n// MarshalJSON implements encoding/json.Marshaler.\nfunc (src CopyInResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType              string\n\t\tColumnFormatCodes []uint16\n\t}{\n\t\tType:              \"CopyInResponse\",\n\t\tColumnFormatCodes: src.ColumnFormatCodes,\n\t})","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/jackc/pgx/blob/ec1a0befd22592cffffdeeb0a50311b506372f4c/pgproto3/copy_in_response.go#L35-L71","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Cap ColumnFormatCodes at 65535; a single-entry slice is valid and applies the format to all columns.","Audit the construction loop for an unbounded or mis-scoped append.","If proxying, forward the server's raw encoded bytes instead of decoding and re-encoding.","Verify the column count source is not a corrupted value read from elsewhere."],"exampleFix":"// before\nresp := &pgproto3.CopyInResponse{\n    OverallFormat:     0,\n    ColumnFormatCodes: bigSlice, // > 65535\n}\n_, err := resp.Encode(nil)\n\n// after\nresp := &pgproto3.CopyInResponse{\n    OverallFormat:     0,\n    ColumnFormatCodes: []uint16{0},\n}\n_, err := resp.Encode(nil)","handlingStrategy":"validation","validationCode":"func validateCopyInResponseEncode(r *pgproto3.CopyInResponse) error {\n\tif len(r.ColumnFormatCodes) > math.MaxUint16 {\n\t\treturn fmt.Errorf(\"too many column format codes: %d (max %d)\", len(r.ColumnFormatCodes), math.MaxUint16)\n\t}\n\treturn nil\n}","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Use a one-element ColumnFormatCodes slice for uniform formats.","Cap column counts at 65535.","Forward server bytes verbatim in proxies."],"tags":["pgproto3","protocol","encoding","copy","wire-protocol","validation"],"analyzedSha":"ec1a0befd22592cffffdeeb0a50311b506372f4c","analyzedAt":"2026-08-04T22:52:11.263Z","schemaVersion":2}