jackc/pgx · error
bad cancel request code
Error message
bad cancel request code
What it means
Returned by CancelRequest.Decode when the first 4 bytes of the body do not equal the cancel request magic number 80877102. That magic (0x04D2162E) is how PostgreSQL distinguishes a cancel request from an SSL/GSS request or a normal startup message on the same socket. A mismatch means the bytes are not a cancel request at all.
Source
Thrown at pgproto3/cancel_request.go:32
type CancelRequest struct {
ProcessID uint32
SecretKey []byte
}
// Frontend identifies this message as sendable by a PostgreSQL frontend.
func (*CancelRequest) Frontend() {}
func (dst *CancelRequest) Decode(src []byte) error {
if len(src) < 12 {
return errors.New("cancel request too short")
}
if len(src) > 264 {
return errors.New("cancel request too long")
}
requestCode := binary.BigEndian.Uint32(src)
if requestCode != cancelRequestCode {
return errors.New("bad cancel request code")
}
dst.ProcessID = binary.BigEndian.Uint32(src[4:])
dst.SecretKey = make([]byte, len(src)-8)
copy(dst.SecretKey, src[8:])
return nil
}
// Encode encodes src into dst. dst will include the 4 byte message length.
func (src *CancelRequest) Encode(dst []byte) ([]byte, error) {
if len(src.SecretKey) > 256 {
return nil, errors.New("secret key too long")
}
msgLen := int32(12 + len(src.SecretKey))
dst = pgio.AppendInt32(dst, msgLen)
dst = pgio.AppendInt32(dst, cancelRequestCode)
dst = pgio.AppendUint32(dst, src.ProcessID)View on GitHub (pinned to ec1a0befd2)
Solutions
- Before calling CancelRequest.Decode, switch on the first uint32: 80877102 → cancel, 80877103 → SSL request, 80877104 → GSS request, else → startup/startup-packet path.
- Verify you are reading bytes in network (big-endian) order; the code constant is defined relative to big-endian wire bytes.
- Confirm the sender is actually issuing a cancel (libpq's PQcancel / pg_cancel_backend), not a startup or SSL negotiation.
- If proxying, ensure connection state is tracked so a cancel is only routed to the cancel decoder after the magic has been identified.
Example fix
// before
var cr pgproto3.CancelRequest
err := cr.Decode(body) // body was actually an SSL request
// after
code := binary.BigEndian.Uint32(body)
switch code {
case 80877102: // cancelRequestCode
var cr pgproto3.CancelRequest
err := cr.Decode(body)
case 80877103: // SSL request
case 80877104: // GSS request
default: // startup message
} Defensive patterns
Strategy: validation
Validate before calling
func dispatchStartupCode(body []byte) error {
if len(body) < 4 {
return errors.New("frame too short for startup code")
}
switch binary.BigEndian.Uint32(body) {
case 80877102: // cancel
var cr pgproto3.CancelRequest
return cr.Decode(body)
case 80877103: // SSL
case 80877104: // GSS
default: // startup
}
return nil
} Type guard
null
Try / catch
null
Prevention
- Switch on the first uint32 of a fresh connection before choosing a decoder — never assume the message type.
- Read the magic in big-endian order; the constants are defined against wire bytes.
- Track per-connection state in a proxy so cancels/SSL/GSS/startup are routed correctly.
When it happens
Trigger: `(*CancelRequest).Decode(src)` where `binary.BigEndian.Uint32(src) != 80877102`. Happens when a buffer containing a startup message, SSL request (80877103), or GSS request (80877104) is mistakenly routed to the cancel-request decoder.
Common situations: A proxy or test server reads the first int32 from a fresh connection and dispatches to CancelRequest.Decode instead of inspecting the code first. Also occurs with endianness mistakes (e.g. reading little-endian on a little-endian host) or when the connection bytes were corrupted in transit.
Related errors
- cancel request too short
- cancel request too long
- authentication message too short
- bad gss encoding request code
- secret key too long
AI-assisted analysis of jackc/pgx@ec1a0befd2 (2026-08-04).
Data as JSON: /data/errors/dbd995754d206782.json.
Report an issue: GitHub.