wavetermdev/waveterm · error

request packets may not have both reqid and resid set

Error message

request packets may not have both reqid and resid set

What it means

RpcMessage.Validate enforces that a message is either a request (ReqId) or a response (ResId), never both. A message with both is ambiguous — the router could not tell if it is a new request or a reply. This is a protocol sanity check on inbound/outbound packets.

Source

Thrown at pkg/wshutil/wshrpc.go:147

	ReqId    string `json:"reqid,omitempty"`
	ResId    string `json:"resid,omitempty"`
	Timeout  int64  `json:"timeout,omitempty"`
	Route    string `json:"route,omitempty"`  // to route/forward requests to alternate servers
	Source   string `json:"source,omitempty"` // source route id
	Cont     bool   `json:"cont,omitempty"`   // flag if additional requests/responses are forthcoming
	Cancel   bool   `json:"cancel,omitempty"` // used to cancel a streaming request or response (sent from the side that is not streaming)
	Error    string `json:"error,omitempty"`
	DataType string `json:"datatype,omitempty"`
	Data     any    `json:"data,omitempty"`
}

func (r *RpcMessage) IsRpcRequest() bool {
	return r.Command != "" || r.ReqId != ""
}

func (r *RpcMessage) Validate() error {
	if r.ReqId != "" && r.ResId != "" {
		return fmt.Errorf("request packets may not have both reqid and resid set")
	}
	if r.Cancel {
		if r.Command != "" {
			return fmt.Errorf("cancel packets may not have command set")
		}
		if r.ReqId == "" && r.ResId == "" {
			return fmt.Errorf("cancel packets must have reqid or resid set")
		}
		if r.Data != nil {
			return fmt.Errorf("cancel packets may not have data set")
		}
		return nil
	}
	if r.Command != "" {
		if r.ResId != "" {
			return fmt.Errorf("command packets may not have resid set")
		}
		if r.Error != "" {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Clear ResId if the message is a request (r.ResId = "")
  2. Clear ReqId if the message is actually a response (r.ReqId = "")
  3. Fix the sender code so responses never carry ReqId and vice versa
  4. Validate messages at construction time with Validate() before sending

Example fix

// before
msg := wshutil.RpcMessage{ReqId: "r1", ResId: "res1"}
// after
msg := wshutil.RpcMessage{ReqId: "r1"} // request only
Defensive patterns

Strategy: validation

Validate before calling

if msg.ReqId != "" && msg.ResId != "" {
	msg.ResId = "" // treat as request
}
if err := msg.Validate(); err != nil { return err }

Type guard

func isPureRequest(msg wshutil.RpcMessage) bool {
	return msg.ReqId != "" && msg.ResId == ""
}

Try / catch

if err := msg.Validate(); err != nil {
	return fmt.Errorf("dropping malformed rpc packet: %w", err)
}

Prevention

When it happens

Trigger: Calling Validate on an RpcMessage where both ReqId and ResId are non-empty strings.

Common situations: A producer accidentally copies ResId into a new request; a relay/gateway that mutates responses and re-sends them as requests; corrupted or hand-crafted JSON messages injected into the wire.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/2d6a2508f7cb369a. Report an issue: GitHub.