wavetermdev/waveterm · error

no route for %q

Error message

no route for %q

What it means

WshRouter routes RPCs and events by routeId. noRouteErr produces this error when a message arrives (or is sent) for a routeId that has no registered route and no default route is configured; an empty routeId yields the plainer "no default route" error. It indicates the wsh message could not be delivered to any handler.

Source

Thrown at pkg/wshutil/wshrouter.go:196

func (router *WshRouter) SetAsRootRouter() {
	router.lock.Lock()
	defer router.lock.Unlock()
	router.isRootRouter = true

	// also bind $control:root to the control RPC
	linkId := router.routeMap[ControlRoute]
	if linkId != baseds.NoLinkId {
		router.routeMap[ControlRootRoute] = linkId
		log.Printf("wshrouter registered control:root route linkid=%d", linkId)
	}
}

func noRouteErr(routeId string) error {
	if routeId == "" {
		return errors.New("no default route")
	}
	return fmt.Errorf("no route for %q", routeId)
}

func (router *WshRouter) SendEvent(routeId string, event wps.WaveEvent) {
	defer func() {
		panichandler.PanicHandler("WshRouter.SendEvent", recover())
	}()
	lm := router.getLinkForRoute(routeId)
	if lm == nil {
		return
	}
	msg := RpcMessage{
		Command: wshrpc.Command_EventRecv,
		Route:   routeId,
		Data:    event,
	}
	msgBytes, err := json.Marshal(msg)
	if err != nil {
		// nothing to do

View on GitHub (pinned to a4447c1563)

Solutions

  1. Verify the routeId against the wshrpc.RouteId_* constants and fix typos.
  2. Ensure the remote side has registered the route before sending — wait for connection/route-ready (e.g. retry after registering controllers), or use the default controller route for general commands.
  3. Check that the target block/route still exists (the block may have been closed, unregistering its route); reacquire a valid routeId.
  4. If routing to your own endpoint, call router.RegisterRoute(routeId, processor) before sending traffic to it.

Example fix

// before: guessing a route id
client.SendRpcRequest(ctx, "routeservice", req) // no route for "routeservice"
// after: use the declared constant / register first
err := client.SendRpcRequest(ctx, wshrpc.RouteId_Controller, req)
// or, for a custom endpoint:
router.RegisterRoute("routeservice", myProcessor)
err := client.SendRpcRequest(ctx, "routeservice", req)
Defensive patterns

Strategy: retry

Validate before calling

// Confirm the route is registered before sending:
if !routerHasRoute(routeId) {
    return fmt.Errorf("route %s not yet registered; wait for connection", routeId)
}

Try / catch

err := client.SendRpcRequest(ctx, routeId, req)
if err != nil && (strings.Contains(err.Error(), "no route for") || strings.Contains(err.Error(), "no default route")) {
    // brief backoff then re-resolve the route and retry once
    time.Sleep(250 * time.Millisecond)
    routeId = resolveRoute()
    return client.SendRpcRequest(ctx, routeId, req)
}

Prevention

When it happens

Trigger: Sending an RPC/event with SendRpcRequest/SendEvent to a routeId that was never registered (RegisterRoute) or was already unregistered (UnregisterRoute); specifying a route before the remote side finished setting up its routes; a typo in a RouteId.

Common situations: Calling a server command before the websocket/controller connection is established so the route is not yet registered; a remote block/terminal closed so its route disappeared; hand-writing route strings instead of using wshrpc.RouteId_* constants; race between route registration and the first RPC after connection.

Related errors


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