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
- Increase the timeout via the request options (or use a noTimeout/streaming variant) for long-running commands.
- Ensure the server-side handler always responds, including error paths (SendResponseError).
- Verify the route/target exists and is alive; check connection state between the two endpoints.
- Add debug logging (EnableRpcDebug) on both ends to see whether the request was received and processed.
- 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
- Set explicit TimeoutMs for long-running commands instead of relying on the default.
- Ensure every server handler responds on all paths, including errors (SendResponseError).
- Monitor connection health; a dead peer guarantees client timeouts.
- Use debug logging (EnableRpcDebug) to trace request/response round trips when timeouts appear.
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- failed to list workspaces: %v
- failed to list blocks from all %d workspace(s)
- reinstalling connection: %w
- disconnecting %q error: %w
- ensuring connection: %w
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/3bc94dae64caaa35.
Report an issue: GitHub.