{"id":"1a27e69b22b7ab24","repo":"jackc/pgx","slug":"too-many-column-format-codes","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_both_response.go","lineNumber":52,"sourceCode":"\t\treturn &invalidMessageFormatErr{messageType: \"CopyBothResponse\"}\n\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 = CopyBothResponse{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 *CopyBothResponse) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'W')\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 CopyBothResponse) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(struct {\n\t\tType              string\n\t\tColumnFormatCodes []uint16\n\t}{\n\t\tType:              \"CopyBothResponse\",\n\t\tColumnFormatCodes: src.ColumnFormatCodes,\n\t})","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/jackc/pgx/blob/ec1a0befd22592cffffdeeb0a50311b506372f4c/pgproto3/copy_both_response.go#L34-L70","documentation":"Returned by CopyBothResponse.Encode when ColumnFormatCodes has more than 65535 entries. The wire format encodes the column count as a uint16, so anything beyond MaxUint16 cannot be represented. This guard prevents emitting a silently-truncated count that the receiver would misinterpret. CopyBothResponse is the 'W' message used in logical/physical streaming replication.","triggerScenarios":"Calling `(*CopyBothResponse).Encode(dst)` with `len(ColumnFormatCodes) > 65535`. Realistic only in a test harness, a replication proxy synthesising a response, or a bug that builds a per-column slice for a table with >64k columns.","commonSituations":"Almost never hit by application code — CopyBothResponse is a backend→frontend message normally produced by the server, not by the client. Surfaces when writing a PostgreSQL-compatible proxy/server or a fuzzer that constructs oversized messages. A misconfigured replication proxy that loops while appending format codes could trigger it.","solutions":["Cap ColumnFormatCodes at 65535 entries; if you have more columns you cannot represent them in one CopyBothResponse.","Reuse a single format code for all columns when they share a format — the protocol allows one entry to apply to all.","Audit the code that builds ColumnFormatCodes for an unbounded loop or accidental append inside a row loop.","If proxying a server, forward the server's encoded bytes verbatim instead of re-encoding."],"exampleFix":"// before\nresp := &pgproto3.CopyBothResponse{\n    OverallFormat:     0,\n    ColumnFormatCodes: perColumnCodes, // len > 65535\n}\n_, err := resp.Encode(nil)\n\n// after\nresp := &pgproto3.CopyBothResponse{\n    OverallFormat:     0,\n    ColumnFormatCodes: []uint16{0}, // single code applies to all columns\n}\n_, err := resp.Encode(nil)","handlingStrategy":"validation","validationCode":"func validateCopyBothResponseEncode(r *pgproto3.CopyBothResponse) 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 single-element ColumnFormatCodes slice when all columns share a format.","Cap column counts at 65535 at the construction site.","Forward server bytes verbatim in proxies rather than re-encoding."],"tags":["pgproto3","protocol","encoding","copy","replication","wire-protocol","validation"],"analyzedSha":"ec1a0befd22592cffffdeeb0a50311b506372f4c","analyzedAt":"2026-08-04T22:52:11.263Z","schemaVersion":2}