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.

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: /api/errors/66dc66e4a2e31780. Report an issue: GitHub.

Appendix: 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)