{"id":"dbd995754d206782","repo":"jackc/pgx","slug":"bad-cancel-request-code","errorCode":null,"errorMessage":"bad cancel request code","messagePattern":"bad cancel request code","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pgproto3/cancel_request.go","lineNumber":32,"sourceCode":"type CancelRequest struct {\n\tProcessID uint32\n\tSecretKey []byte\n}\n\n// Frontend identifies this message as sendable by a PostgreSQL frontend.\nfunc (*CancelRequest) Frontend() {}\n\nfunc (dst *CancelRequest) Decode(src []byte) error {\n\tif len(src) < 12 {\n\t\treturn errors.New(\"cancel request too short\")\n\t}\n\tif len(src) > 264 {\n\t\treturn errors.New(\"cancel request too long\")\n\t}\n\n\trequestCode := binary.BigEndian.Uint32(src)\n\tif requestCode != cancelRequestCode {\n\t\treturn errors.New(\"bad cancel request code\")\n\t}\n\n\tdst.ProcessID = binary.BigEndian.Uint32(src[4:])\n\tdst.SecretKey = make([]byte, len(src)-8)\n\tcopy(dst.SecretKey, src[8:])\n\n\treturn nil\n}\n\n// Encode encodes src into dst. dst will include the 4 byte message length.\nfunc (src *CancelRequest) Encode(dst []byte) ([]byte, error) {\n\tif len(src.SecretKey) > 256 {\n\t\treturn nil, errors.New(\"secret key too long\")\n\t}\n\tmsgLen := int32(12 + len(src.SecretKey))\n\tdst = pgio.AppendInt32(dst, msgLen)\n\tdst = pgio.AppendInt32(dst, cancelRequestCode)\n\tdst = pgio.AppendUint32(dst, src.ProcessID)","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/jackc/pgx/blob/ec1a0befd22592cffffdeeb0a50311b506372f4c/pgproto3/cancel_request.go#L14-L50","documentation":"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.","triggerScenarios":"`(*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.","commonSituations":"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.","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."],"exampleFix":"// before\nvar cr pgproto3.CancelRequest\nerr := cr.Decode(body) // body was actually an SSL request\n\n// after\ncode := binary.BigEndian.Uint32(body)\nswitch code {\ncase 80877102: // cancelRequestCode\n    var cr pgproto3.CancelRequest\n    err := cr.Decode(body)\ncase 80877103: // SSL request\ncase 80877104: // GSS request\ndefault: // startup message\n}","handlingStrategy":"validation","validationCode":"func dispatchStartupCode(body []byte) error {\n\tif len(body) < 4 {\n\t\treturn errors.New(\"frame too short for startup code\")\n\t}\n\tswitch binary.BigEndian.Uint32(body) {\n\tcase 80877102: // cancel\n\t\tvar cr pgproto3.CancelRequest\n\t\treturn cr.Decode(body)\n\tcase 80877103: // SSL\n\tcase 80877104: // GSS\n\tdefault: // startup\n\t}\n\treturn nil\n}","typeGuard":"null","tryCatchPattern":"null","preventionTips":["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."],"tags":["pgproto3","protocol","decoding","cancel","wire-protocol","authentication"],"analyzedSha":"ec1a0befd22592cffffdeeb0a50311b506372f4c","analyzedAt":"2026-08-04T22:52:11.263Z","schemaVersion":2}