{"id":"de441029213186d3","repo":"jackc/pgx","slug":"too-many-parameters","errorCode":null,"errorMessage":"too many parameters","messagePattern":"too many parameters","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pgproto3/bind.go","lineNumber":130,"sourceCode":"// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.\nfunc (src *Bind) Encode(dst []byte) ([]byte, error) {\n\tdst, sp := beginMessage(dst, 'B')\n\n\tdst = append(dst, src.DestinationPortal...)\n\tdst = append(dst, 0)\n\tdst = append(dst, src.PreparedStatement...)\n\tdst = append(dst, 0)\n\n\tif len(src.ParameterFormatCodes) > math.MaxUint16 {\n\t\treturn nil, errors.New(\"too many parameter format codes\")\n\t}\n\tdst = pgio.AppendUint16(dst, uint16(len(src.ParameterFormatCodes)))\n\tfor _, fc := range src.ParameterFormatCodes {\n\t\tdst = pgio.AppendInt16(dst, fc)\n\t}\n\n\tif len(src.Parameters) > math.MaxUint16 {\n\t\treturn nil, errors.New(\"too many parameters\")\n\t}\n\tdst = pgio.AppendUint16(dst, uint16(len(src.Parameters)))\n\tfor _, p := range src.Parameters {\n\t\tif p == nil {\n\t\t\tdst = pgio.AppendInt32(dst, -1)\n\t\t\tcontinue\n\t\t}\n\n\t\tdst = pgio.AppendInt32(dst, int32(len(p)))\n\t\tdst = append(dst, p...)\n\t}\n\n\tif len(src.ResultFormatCodes) > math.MaxUint16 {\n\t\treturn nil, errors.New(\"too many result format codes\")\n\t}\n\tdst = pgio.AppendUint16(dst, uint16(len(src.ResultFormatCodes)))\n\tfor _, fc := range src.ResultFormatCodes {\n\t\tdst = pgio.AppendInt16(dst, fc)","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/jackc/pgx/blob/ec1a0befd22592cffffdeeb0a50311b506372f4c/pgproto3/bind.go#L112-L148","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Split the statement so each execution binds <=65535 parameters.","Use pgx CopyFrom / PostgreSQL COPY for bulk inserts.","For large IN-lists, pass values via a temp table, array parameter (ANY(unnest($1::int[]))), or jsonb instead of thousands of placeholders.","Add a unit test asserting placeholder count stays under the limit."],"exampleFix":"// before: 100000 placeholders in one statement\nplaceholders := make([]string, 100000)\nfor i := range placeholders { placeholders[i] = fmt.Sprintf(\"$%d\", i+1) }\nq := \"SELECT * FROM t WHERE id IN (\" + strings.Join(placeholders, \",\") + \")\"\nrows, _ := conn.Exec(ctx, q, args...) // -> \"too many parameters\"\n\n// after: use an array parameter to avoid the placeholder explosion\nrows, _ := conn.Query(ctx, \"SELECT * FROM t WHERE id = ANY($1::int[])\", ids)","handlingStrategy":"validation","validationCode":"const maxBindParams = 65535 // math.MaxUint16\nif len(args) > maxBindParams {\n    return fmt.Errorf(\"query has %d bind parameters, protocol max is %d; split the batch or use COPY/ANY($1::array)\", len(args), maxBindParams)\n}","typeGuard":"func bindParamCountOK(params [][]byte) bool { return len(params) <= 65535 }","tryCatchPattern":"if _, err := bindMsg.Encode(dst); err != nil {\n    if strings.Contains(err.Error(), \"too many parameters\") {\n        // re-issue in chunks of <=65535 params, or switch to COPY / array param\n    }\n}","preventionTips":["Keep placeholder counts under 65535 per execution; chunk large IN-lists and multi-row inserts.","Use pgx CopyFrom for bulk inserts instead of huge parameterized INSERTs.","Replace big IN-lists with ANY($1::type[]) or a temp table.","Unit-test dynamic SQL builders to assert the placeholder ceiling."],"tags":["bind","protocol","encode","parameter-limit","pgproto3"],"analyzedSha":"ec1a0befd22592cffffdeeb0a50311b506372f4c","analyzedAt":"2026-08-04T22:52:11.263Z","schemaVersion":2}