wavetermdev/waveterm · error

invalid packet: must have command, reqid, or resid set

Error message

invalid packet: must have command, reqid, or resid set

What it means

Every RpcMessage must be classifiable as a request (Command set), a response (ResId set), or otherwise carry ReqId. Validate() returns this error when Command, ReqId, and ResId are all empty — the packet has no routing identity and cannot be dispatched.

Source

Thrown at pkg/wshutil/wshrpc.go:194

		}
		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)
	if serverType.Kind() != reflect.Pointer && serverType.Elem().Kind() != reflect.Struct {
		panic(fmt.Sprintf("serverImpl must be a pointer to struct, got %v", serverType))
	}
}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Populate Command (plus ReqId) for requests or ResId/ReqId for responses before sending.
  2. Check that the message actually got serialized/deserialized with the expected fields (field-name/tag mismatches).
  3. Guard send paths with Validate() and skip/log packets that fail rather than pushing them onto OutputCh.
  4. Upgrade both ends of the connection to matching wshutil versions to avoid field-shape drift.

Example fix

// before
w.OutputCh <- &wshutil.WshRpcDataStream{MsgBytes: mustJSON(wshrpc.RpcMessage{})}
// after
w.OutputCh <- &wshutil.WshRpcDataStream{MsgBytes: mustJSON(wshrpc.RpcMessage{Command: "waveclient:get", ReqId: genId()})}
Defensive patterns

Strategy: validation

Validate before calling

func checkPacketIdentity(m wshrpc.RpcMessage) error {
    if m.Command == "" && m.ReqId == "" && m.ResId == "" {
        return fmt.Errorf("packet has no command/reqid/resid")
    }
    return nil
}

Type guard

func hasIdentity(m wshrpc.RpcMessage) bool {
    return m.Command != "" || m.ReqId != "" || m.ResId != ""
}

Prevention

When it happens

Trigger: Sending a zero-value RpcMessage, an empty payload, or a message whose meaningful fields live only in nested data without any of the three required fields set.

Common situations: Uninitialized structs passed to the writer; JSON round-trips that dropped fields due to schema mismatch between versions; generic forwarding code that passes through empty envelopes.

Related errors


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