jackc/pgx · error

secret key too long

Error message

secret key too long

What it means

Returned by CancelRequest.Encode when SecretKey is longer than 256 bytes. The wire format reserves a single byte for nothing and the server caps the secret key at 256 bytes, so the library refuses to emit a message the server would reject or misparse. This is a caller-side construction error, not a network problem.

Source

Thrown at pgproto3/cancel_request.go:45

		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)
	dst = append(dst, src.SecretKey...)
	return dst, nil
}

// MarshalJSON implements encoding/json.Marshaler.
func (src CancelRequest) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Type      string
		ProcessID uint32
		SecretKey string
	}{
		Type:      "CancelRequest",
		ProcessID: src.ProcessID,

View on GitHub (pinned to ec1a0befd2)

Solutions

  1. Set SecretKey to the 4-byte secret key from the server's BackendKeyData message (the SecretKey field on pgproto3.BackendKeyData), nothing larger.
  2. Validate before encoding: `if len(req.SecretKey) > 256 { return ... }` (or, more correctly, assert it is 4 bytes).
  3. Double-check you did not assign ProcessID or a PID slice into SecretKey by mistake.
  4. If you need a larger cancel token, that is not supported by the PostgreSQL wire protocol — redesign the caller.

Example fix

// before
req := &pgproto3.CancelRequest{
    ProcessID: backendKey.ProcessID,
    SecretKey: fullBackendKeyBytes, // 8 bytes, wrong
}
_, err := req.Encode(nil)

// after
req := &pgproto3.CancelRequest{
    ProcessID: backendKey.ProcessID,
    SecretKey: mustSecretKey(backendKey.SecretKey), // exactly 4 bytes
}
_, err := req.Encode(nil)
Defensive patterns

Strategy: validation

Validate before calling

func validateCancelRequestEncode(req *pgproto3.CancelRequest) error {
	if len(req.SecretKey) > 256 {
		return fmt.Errorf("secret key too long: %d bytes (max 256)", len(req.SecretKey))
	}
	return nil
}

// also assert the realistic 4-byte key from BackendKeyData:
// req.SecretKey must come from backendKeyData.SecretKey (4 bytes)

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Calling `(*CancelRequest).Encode(dst)` on a struct where `len(SecretKey) > 256`. The real PostgreSQL secret key is exactly 4 bytes (a uint32), so any large slice indicates a caller bug.

Common situations: A caller copies the entire BackendKeyData or a full process credential blob into SecretKey instead of the 4-byte secret key returned by the server in the BackendKeyData message. Also seen in test code that stuffs an arbitrary token into the field.

Related errors


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