wavetermdev/waveterm · error

timeout sending request

Error message

timeout sending request

What it means

SendComplexRequest registers the request handler and then writes the request bytes to OutputCh; if the handler's context is done before the write is accepted, registration is finalized and this error is returned — the request never made it onto the wire.

Source

Thrown at pkg/wshutil/wshrpc.go:807

	}
	req := &RpcMessage{
		Command: command,
		ReqId:   handler.reqId,
		Data:    data,
		Timeout: timeoutMs,
		Route:   opts.Route,
	}
	barr, err := json.Marshal(req)
	if err != nil {
		return nil, err
	}
	handler.respCh = w.registerRpc(handler, command, opts.Route, handler.reqId)
	select {
	case w.OutputCh <- barr:
		return handler, nil
	case <-handler.ctx.Done():
		handler.finalize()
		return nil, fmt.Errorf("timeout sending request")
	}
}

func (w *WshRpc) IsServerDone() bool {
	w.Lock.Lock()
	defer w.Lock.Unlock()
	return w.ServerDone
}

func (w *WshRpc) setServerDone() {
	w.Lock.Lock()
	defer w.Lock.Unlock()
	w.ServerDone = true
	close(w.CtxDoneCh)
	utilfn.DrainChannelSafe(w.InputCh, "wshrpc.setServerDone")
}

func (w *WshRpc) retrySendTimeout(resId string) {

View on GitHub (pinned to a4447c1563)

Solutions

  1. Check ctx.Err() before sending and avoid issuing requests with cancelled/expired contexts.
  2. Increase the timeout via RpcOpts (TimeoutMs) if the output channel can be slow.
  3. Ensure the connection read loop keeps running and draining OutputCh during the request.
  4. Retry the request on a fresh, live connection/context after verifying the peer is up.

Example fix

// before
ctx, cancel := context.WithTimeout(ctx, 50*time.Millisecond)
resp, err := w.SendComplexRequest("waveclient:get", args, &wshutil.RpcOpts{Route: route})
// after
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
resp, err := w.SendComplexRequest("waveclient:get", args, &wshutil.RpcOpts{Route: route, TimeoutMs: 5000})
Defensive patterns

Strategy: try-catch

Validate before calling

if ctx.Err() != nil {
    return fmt.Errorf("cannot send request: context already done: %w", ctx.Err())
}

Try / catch

handler, err := w.SendComplexRequest(cmd, args, opts)
if err != nil && strings.Contains(err.Error(), "timeout sending request") {
    // output channel blocked or ctx expired; check connection health, then retry with a longer timeout
    return retryWithBackoff(func() error { _, err := w.SendComplexRequest(cmd, args, opts); return err })
}

Prevention

When it happens

Trigger: Calling a request with a context that is already cancelled/expired; connection shutdown closing OutputCh/consumer while the request is being sent; per-request deadline shorter than the time needed to enqueue the packet.

Common situations: Firing RPCs during teardown of a block/connection; callers passing an already-cancelled context; timeouts configured too aggressively (or DefaultTimeoutMs too small for a congested output channel).

Understand the failure class

Related errors


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