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
- Clear ResId if the message is a request (r.ResId = "")
- Clear ReqId if the message is actually a response (r.ReqId = "")
- Fix the sender code so responses never carry ReqId and vice versa
- 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
- Build requests and responses with separate constructors
- Never mutate a response message into a request in place
- Call Validate() before every send in development builds
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
- command packets may not have resid set
- blockid is required
- size must be greater than 0
- invalid context, must have a routeid
- cancel packets may not have command set
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/2d6a2508f7cb369a.
Report an issue: GitHub.