jackc/pgx · error

unexpected trailing bytes at offset

Error message

%d unexpected trailing bytes at offset %d

What it means

Reader.Finish found unread bytes after a decoder consumed what it expected — the message contains trailing data beyond the declared structure. This signals a length mismatch between the message header and its decoded content.

Solutions

  1. Verify the message length prefix matches the encoded body
  2. Check the decoder consumes every field the message contains
  3. Compare against a reference decode of the same message
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at internal/pgio/read.go:208 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of jackc/pgx@ec1a0befd2 (2026-08-04). Data as JSON: /api/errors/21e71eff5b030dcb. Report an issue: GitHub.

Appendix: source

Thrown at internal/pgio/read.go:208

		return nil, false
	}
	return r.Bytes(int(length)), false
}

// 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.

View on GitHub (pinned to ec1a0befd2)