{"record":{"id":"12f8978abddf3ec1","repo":"wavetermdev/waveterm","slug":"cancel-packets-may-not-have-command-set","errorCode":null,"errorMessage":"cancel packets may not have command set","messagePattern":"cancel packets may not have command set","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wshutil/wshrpc.go","lineNumber":151,"sourceCode":"\tSource   string `json:\"source,omitempty\"` // source route id\n\tCont     bool   `json:\"cont,omitempty\"`   // flag if additional requests/responses are forthcoming\n\tCancel   bool   `json:\"cancel,omitempty\"` // used to cancel a streaming request or response (sent from the side that is not streaming)\n\tError    string `json:\"error,omitempty\"`\n\tDataType string `json:\"datatype,omitempty\"`\n\tData     any    `json:\"data,omitempty\"`\n}\n\nfunc (r *RpcMessage) IsRpcRequest() bool {\n\treturn r.Command != \"\" || r.ReqId != \"\"\n}\n\nfunc (r *RpcMessage) Validate() error {\n\tif r.ReqId != \"\" && r.ResId != \"\" {\n\t\treturn fmt.Errorf(\"request packets may not have both reqid and resid set\")\n\t}\n\tif r.Cancel {\n\t\tif r.Command != \"\" {\n\t\t\treturn fmt.Errorf(\"cancel packets may not have command set\")\n\t\t}\n\t\tif r.ReqId == \"\" && r.ResId == \"\" {\n\t\t\treturn fmt.Errorf(\"cancel packets must have reqid or resid set\")\n\t\t}\n\t\tif r.Data != nil {\n\t\t\treturn fmt.Errorf(\"cancel packets may not have data set\")\n\t\t}\n\t\treturn nil\n\t}\n\tif r.Command != \"\" {\n\t\tif r.ResId != \"\" {\n\t\t\treturn fmt.Errorf(\"command packets may not have resid set\")\n\t\t}\n\t\tif r.Error != \"\" {\n\t\t\treturn fmt.Errorf(\"command packets may not have error set\")\n\t\t}\n\t\tif r.DataType != \"\" {\n\t\t\treturn fmt.Errorf(\"command packets may not have datatype set\")","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wshutil/wshrpc.go#L133-L169","documentation":"Packet validation error: CANCEL packets must not carry a command. Cancelling identifies the target solely by resid; setting command on a cancel packet violates the wire protocol and the packet is rejected.","triggerScenarios":"Calling Validate on an RpcMessage with Cancel=true and Command set to a non-empty string.","commonSituations":"A caller builds a normal command message then flips Cancel=true to cancel it instead of clearing the command; generic message-mutation helpers that toggle Cancel without scrubbing other fields.","solutions":["Remove the Command field when canceling (send only Cancel + the target ReqId/ResId)","Construct the cancel message fresh: RpcMessage{Cancel: true, ReqId: originalReqId}","Check code paths that set Cancel=true so they do not leave Command populated"],"exampleFix":"// before\nmsg := wshutil.RpcMessage{Cancel: true, Command: \"controller.sendinput\", ReqId: origId}\n// after\nmsg := wshutil.RpcMessage{Cancel: true, ReqId: origId}","handlingStrategy":"validation","validationCode":"if msg.Cancel && msg.Command != \"\" {\n\tmsg.Command = \"\"\n}\nif err := msg.Validate(); err != nil { return err }","typeGuard":"func isWellFormedCancel(msg wshutil.RpcMessage) bool {\n\treturn !msg.Cancel || (msg.Command == \"\" && (msg.ReqId != \"\" || msg.ResId != \"\") && msg.Data == nil)\n}","tryCatchPattern":"if err := msg.Validate(); err != nil {\n\treturn fmt.Errorf(\"invalid cancel packet: %w\", err)\n}","preventionTips":["Use a dedicated CancelRequest(reqId) helper","Do not toggle Cancel on an existing command message","Keep control packets minimal — only Cancel + id fields"],"tags":["wsh","rpc","validation","cancel"],"backgroundTag":"invalid-rpc-packet","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}