jackc/pgx · error

too many arg format codes

Error message

too many arg format codes

What it means

Returned by FunctionCall.Encode when ArgFormatCodes has more than 65535 entries. The count is encoded as a uint16, so MaxUint16 is the ceiling; the guard prevents a truncated count. FunctionCall is the legacy 'F' message (the v3 function-call protocol), where each entry is 0 (text) or 1 (binary).

Source

Thrown at pgproto3/function_call.go:103

	// The format code for the function result. Must presently be zero (text) or one (binary).
	if len(src[rp:]) < 2 {
		return &invalidMessageFormatErr{messageType: "FunctionCall"}
	}
	resultFormatCode := binary.BigEndian.Uint16(src[rp:])
	if resultFormatCode != 0 && resultFormatCode != 1 {
		return &invalidMessageFormatErr{messageType: "FunctionCall"}
	}
	dst.ResultFormatCode = resultFormatCode
	return nil
}

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

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

	if len(src.Arguments) > math.MaxUint16 {
		return nil, errors.New("too many arguments")
	}
	dst = pgio.AppendUint16(dst, uint16(len(src.Arguments)))
	for _, argument := range src.Arguments {
		if argument == nil {
			dst = pgio.AppendInt32(dst, -1)
		} else {
			dst = pgio.AppendInt32(dst, int32(len(argument)))
			dst = append(dst, argument...)
		}
	}

View on GitHub (pinned to ec1a0befd2)

Solutions

  1. Cap ArgFormatCodes at 65535; a single-entry slice applies the format to all arguments (the protocol explicitly allows this).
  2. Audit the construction loop for accidental duplication or per-row instead of per-arg appends.
  3. Prefer a single format code shared by all arguments when formats are uniform.
  4. Pre-validate length before encoding and return a clear error.

Example fix

// before
fc := &pgproto3.FunctionCall{
    Function:       oid,
    ArgFormatCodes: perArgCodes, // > 65535
}
_, err := fc.Encode(nil)

// after
fc := &pgproto3.FunctionCall{
    Function:       oid,
    ArgFormatCodes: []uint16{0}, // one code applies to all args
}
_, err := fc.Encode(nil)
Defensive patterns

Strategy: validation

Validate before calling

func validateFunctionCallFormatCodes(fc *pgproto3.FunctionCall) error {
	if len(fc.ArgFormatCodes) > math.MaxUint16 {
		return fmt.Errorf("too many arg format codes: %d (max %d)", len(fc.ArgFormatCodes), math.MaxUint16)
	}
	return nil
}

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Calling `(*FunctionCall).Encode(dst)` with `len(ArgFormatCodes) > 65535`. Realistic only in synthetic code, a fuzzer, or a caller that builds one format code per argument for a function with >64k args.

Common situations: The function-call protocol is rarely used by modern applications (prepared statements are preferred). A caller migrating an old libpq-style codebase that calls a function with a huge argument list, or a test harness stress-testing the encoder, could hit this. More often it signals a loop that over-appends.

Related errors


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