wavetermdev/waveterm · error

EC-TIME: timeout waiting for response

Error message

EC-TIME: timeout waiting for response

What it means

In runServer, when the per-request timeout fires (CtxDoneCh signals the registered response id), the pending RPC is unregistered with this error. The caller of the request (SendComplexRequest/ SendSimpleRequest path) receives "EC-TIME: timeout waiting for response" because the remote side never responded within the timeout window.

Source

Thrown at pkg/wshutil/wshrpc.go:405

outer:
	for {
		var inputVal baseds.RpcInputChType
		var inputChMore bool
		var resIdTimeout string

		select {
		case inputVal, inputChMore = <-w.InputCh:
			if !inputChMore {
				break outer
			}
			if w.Debug {
				log.Printf("[%s] received message: %s\n", w.DebugName, string(inputVal.MsgBytes))
			}
		case resIdTimeout = <-w.CtxDoneCh:
			if w.Debug {
				log.Printf("[%s] received request timeout: %s\n", w.DebugName, resIdTimeout)
			}
			w.unregisterRpc(resIdTimeout, fmt.Errorf("EC-TIME: timeout waiting for response"))
			continue
		}

		var msg RpcMessage
		err := json.Unmarshal(inputVal.MsgBytes, &msg)
		if err != nil {
			log.Printf("wshrpc received bad message: %v\n", err)
			continue
		}
		if msg.Cancel {
			if msg.ReqId != "" {
				w.cancelRequest(msg.ReqId)
			}
			continue
		}
		if msg.IsRpcRequest() {
			// Handle stream commands synchronously since the broker is designed to be non-blocking.
			// RecvData/RecvAck just enqueue to work queues, so there's no risk of blocking the main loop.

View on GitHub (pinned to a4447c1563)

Solutions

  1. Increase the timeout via the request options (or use a noTimeout/streaming variant) for long-running commands.
  2. Ensure the server-side handler always responds, including error paths (SendResponseError).
  3. Verify the route/target exists and is alive; check connection state between the two endpoints.
  4. Add debug logging (EnableRpcDebug) on both ends to see whether the request was received and processed.
  5. Retry the request after checking the peer is responsive.

Example fix

// before
resp, err := w.SendComplexRequest("longop", args, &wshutil.RpcOpts{Route: route})
// after
resp, err := w.SendComplexRequest("longop", args, &wshutil.RpcOpts{Route: route, TimeoutMs: 60000})
Defensive patterns

Strategy: try-catch

Validate before calling

if w == nil || !w.IsServerDone() == false {
    return fmt.Errorf("rpc server already shut down, skipping request")
}

Type guard

func peerAlive(w *wshutil.WshRpc) bool {
    return w != nil && !w.IsServerDone()
}

Try / catch

handler, err := w.SendComplexRequest(cmd, args, &wshutil.RpcOpts{Route: route, TimeoutMs: 30000})
if err != nil {
    if strings.Contains(err.Error(), "EC-TIME") {
        // increase timeout or check remote handler responsiveness, then retry once
    }
    return err
}

Prevention

When it happens

Trigger: Calling an RPC whose handler never calls SendResponse/SendResponseError before the configured (or default DefaultTimeoutMs) timeout elapses; remote peer hung, slow, or connection dead; the noTimeout/streaming request not being used for long operations.

Common situations: Long-running commands (heavy terminal/file ops) exceeding the default timeout; a blocked remote block controller; network disconnect leaving the request pending; calling an RPC route that no one serves.

Understand the failure class

Related errors


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