jackc/pgx · error

invalid length

Error message

invalid length

What it means

ErrInvalidLength is the sentinel (internal/pgio/read.go:213) wrapped by the Uint16Exact/Uint32Exact/Uint64Exact helpers when src is not exactly 2/4/8 bytes. These helpers are the scan plans for fixed-size built-in types (int2/int4/int8/float4/float8 and friends); they assert the server's binary payload length matches the type size, since for an unstructured scalar there is nothing else to check.

Source

Thrown at internal/pgio/read.go:213

// Finish returns the first error encountered, or an error if unread bytes
// remain. Decoders that must consume the entire source should end with Finish.
func (r *Reader) Finish() error {
	if r.err != nil {
		return r.err
	}
	if r.rp != len(r.s) {
		return r.errTrailing()
	}
	return nil
}

func (r *Reader) errTrailing() error {
	return fmt.Errorf("%d unexpected trailing bytes at offset %d", len(r.s)-r.rp, r.rp)
}

// ErrInvalidLength is wrapped by the errors returned from the exact-length
// read functions below.
var ErrInvalidLength = errors.New("invalid length")

func errLength(want, got int) error {
	return fmt.Errorf("%w: expected %d bytes, got %d", ErrInvalidLength, want, got)
}

// The Uint*Exact functions read a single fixed-size value that makes up an
// entire message, which is what the scan plans for the fixed-size PostgreSQL
// types receive. They are the counterpart to Reader for values that have no
// internal structure: there is no position to track and no error to make
// sticky, just an exact-length assertion the caller cannot skip. Keeping them
// separate from Reader is deliberate — these are the hottest decode paths in
// the driver and they are small enough for the compiler to inline, which a
// Reader method carrying a bounds check and a read pointer is not.

// Uint16Exact returns the big-endian uint16 in src, which must be exactly 2 bytes.
func Uint16Exact(src []byte) (uint16, error) {
	if len(src) != 2 {
		return 0, errLength(2, len(src))

View on GitHub (pinned to ec1a0befd2)

Solutions

  1. Confirm the column type OID is mapped to the correct codec in your TypeMap (a wrong OID→codec mapping produces wrong-length reads).
  2. Capture the failing column's typname and the raw src length to confirm the server payload is genuinely mis-sized.
  3. Update pgx to a release matching your server version.
  4. For custom scalars, do not reuse the *Exact helpers unless your binary width is exactly 2/4/8; write a sized Codec instead.

Example fix

// before — wrong exact helper for a custom 16-byte type
plans := pgtype.ScanPlanFuncs{}
plans.Register(oid, func(m *pgtype.Map, src []byte) (any, error) {
    return pgio.Uint32Exact(src) // wrong size -> "invalid length"
})

// after — sized codec matching the real binary width
func (c *MyCodec) DecodeBinary(m *pgtype.Map, src []byte) (any, error) {
    if len(src) != 16 { return nil, fmt.Errorf("bad len %d", len(src)) }
    /* decode 16 bytes */
}
Defensive patterns

Strategy: try-catch

Type guard

func isInvalidLength(err error) bool { return errors.Is(err, pgio.ErrInvalidLength) }

Try / catch

v, err := plan.Scan(typeMap, src)
if errors.Is(err, pgio.ErrInvalidLength) {
    // wrong-length binary payload for a fixed-size type
    log.Printf("invalid length for oid=%d: got %d bytes", oid, len(src))
    return nil, err
}

Prevention

When it happens

Trigger: Scanning a fixed-size scalar column whose binary representation arrived with the wrong byte length: e.g. an int4 column with a 3- or 8-byte payload, or NULL handled incorrectly so an empty/foreign slice is passed to the Exact helper.

Common situations: Server/extension bug returning a wrong-length payload; a codec/type-map misregistration pointing an OID at the wrong Exact helper (e.g. int8 OID mapped to Uint32Exact); corrupted bytes from a proxy; misuse of a custom Codec that forwards the wrong src.

Related errors


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