wavetermdev/waveterm · warning

timeout sending response

Error message

timeout sending response

What it means

SendResponse marshals the response and writes it to the WshRpc OutputCh; if the handler's context is done before the write completes, the response could not be delivered and this error is returned. The requester has typically timed out, cancelled, or the connection closed.

Source

Thrown at pkg/wshutil/wshrpc.go:707

		defer handler.close()
	}
	if handler.reqId == "" {
		return nil
	}
	msg := &RpcMessage{
		ResId: handler.reqId,
		Data:  data,
		Cont:  !done,
	}
	barr, err := json.Marshal(msg)
	if err != nil {
		return err
	}
	select {
	case handler.w.OutputCh <- barr:
		return nil
	case <-handler.ctx.Done():
		return fmt.Errorf("timeout sending response")
	}
}

func (handler *RpcResponseHandler) SendResponseError(err error) {
	defer func() {
		panichandler.PanicHandler("SendResponseError", recover())
	}()
	if handler.done.Load() {
		return
	}
	defer handler.close()
	if handler.reqId == "" {
		return
	}
	msg := &RpcMessage{
		ResId: handler.reqId,
		Error: err.Error(),
	}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Check handler.ctx.Err() before doing expensive work or sending; abort early if already done.
  2. Increase the client-side timeout for slow operations so the response window is sufficient.
  3. Treat this as a benign late-response race in the handler (log debug, skip retry) since the caller is gone.
  4. Ensure responses are sent promptly; move heavy work before the response window or use streaming with per-chunk sends.

Example fix

// before
func respond(w *wshutil.WshRpc, handler *wshutil.RpcResponseHandler, data any) error {
    return handler.SendResponse(data, true)
}
// after
func respond(w *wshutil.WshRpc, handler *wshutil.RpcResponseHandler, data any) error {
    if handler.ctx.Err() != nil { return handler.ctx.Err() }
    return handler.SendResponse(data, true)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if handler.ctx.Err() != nil {
    return handler.ctx.Err() // skip sending; requester already gone
}

Try / catch

if err := handler.SendResponse(data, true); err != nil && strings.Contains(err.Error(), "timeout sending response") {
    // requester timed out or disconnected; log at debug, do not retry
    return nil
}

Prevention

When it happens

Trigger: Server handler calls SendResponse after the requester's timeout already fired (unregisterRpc removed the pending entry and cancelled); connection shutdown draining context; OutputCh blocked while the handler ctx expires.

Common situations: Slow handler finishing just past the client timeout; client disconnects mid-request; long streaming operations whose per-chunk sends exceed the remaining deadline.

Understand the failure class

Related errors


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