wavetermdev/waveterm · error

response packets may not have timeout set

Error message

response packets may not have timeout set

What it means

Packet validation error: RESPONSE packets must not carry a timeout. Timeouts are specified by the requester on the command; echoing a timeout on a response is a protocol violation and the packet is rejected.

Source

Thrown at pkg/wshutil/wshrpc.go:190

	}
	if r.ReqId != "" {
		if r.ResId == "" {
			return fmt.Errorf("request packets must have resid set")
		}
		if r.Timeout != 0 {
			return fmt.Errorf("non-command request packets may not have timeout set")
		}
		return nil
	}
	if r.ResId != "" {
		if r.Command != "" {
			return fmt.Errorf("response packets may not have command set")
		}
		if r.ReqId == "" {
			return fmt.Errorf("response packets must have reqid set")
		}
		if r.Timeout != 0 {
			return fmt.Errorf("response packets may not have timeout set")
		}
		return nil
	}
	return fmt.Errorf("invalid packet: must have command, reqid, or resid set")
}

type rpcData struct {
	Command string
	Route   string
	ResCh   chan *RpcMessage
	Handler *RpcRequestHandler
}

func validateServerImpl(serverImpl ServerImpl) {
	if serverImpl == nil {
		return
	}
	serverType := reflect.TypeOf(serverImpl)

View on GitHub (pinned to a4447c1563)

Solutions

  1. Zero out Timeout on any packet carrying ResId before validating/sending.
  2. Build responses from scratch (only ResId, ReqId, data) instead of mutating request messages.
  3. Use the handler-based response APIs (SendResponse/SendComplexRequest) so field ownership is explicit.

Example fix

// before
resp := reqMsg // carries Timeout
resp.ResId = "res-1"
// after
resp := wshrpc.RpcMessage{ResId: "res-1", ReqId: reqMsg.ReqId}
Defensive patterns

Strategy: validation

Validate before calling

func checkResNoTimeout(m wshrpc.RpcMessage) error {
    if m.ResId != "" && m.Timeout != 0 {
        return fmt.Errorf("response packet has timeout set")
    }
    return nil
}

Type guard

func isCleanResponse(m wshrpc.RpcMessage) bool {
    return m.ResId != "" && m.Command == "" && m.Timeout == 0 && m.ReqId != ""
}

Prevention

When it happens

Trigger: Building or copying an RpcMessage where Timeout (or the opts timeout carried into the message) survives from a request into a response packet with ResId set.

Common situations: Cloning a request message and mutating it into a response without clearing Timeout; middleware that forwards all fields of an incoming packet back out; serialization of stale cached messages.

Understand the failure class

Related errors


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