{"record":{"id":"3393816b605d0882","repo":"wavetermdev/waveterm","slug":"timeout-sending-response","errorCode":null,"errorMessage":"timeout sending response","messagePattern":"timeout sending response","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/wshutil/wshrpc.go","lineNumber":707,"sourceCode":"\t\tdefer handler.close()\n\t}\n\tif handler.reqId == \"\" {\n\t\treturn nil\n\t}\n\tmsg := &RpcMessage{\n\t\tResId: handler.reqId,\n\t\tData:  data,\n\t\tCont:  !done,\n\t}\n\tbarr, err := json.Marshal(msg)\n\tif err != nil {\n\t\treturn err\n\t}\n\tselect {\n\tcase handler.w.OutputCh <- barr:\n\t\treturn nil\n\tcase <-handler.ctx.Done():\n\t\treturn fmt.Errorf(\"timeout sending response\")\n\t}\n}\n\nfunc (handler *RpcResponseHandler) SendResponseError(err error) {\n\tdefer func() {\n\t\tpanichandler.PanicHandler(\"SendResponseError\", recover())\n\t}()\n\tif handler.done.Load() {\n\t\treturn\n\t}\n\tdefer handler.close()\n\tif handler.reqId == \"\" {\n\t\treturn\n\t}\n\tmsg := &RpcMessage{\n\t\tResId: handler.reqId,\n\t\tError: err.Error(),\n\t}","sourceCodeStart":689,"sourceCodeEnd":725,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wshutil/wshrpc.go#L689-L725","documentation":"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.","triggerScenarios":"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.","commonSituations":"Slow handler finishing just past the client timeout; client disconnects mid-request; long streaming operations whose per-chunk sends exceed the remaining deadline.","solutions":["Check handler.ctx.Err() before doing expensive work or sending; abort early if already done.","Increase the client-side timeout for slow operations so the response window is sufficient.","Treat this as a benign late-response race in the handler (log debug, skip retry) since the caller is gone.","Ensure responses are sent promptly; move heavy work before the response window or use streaming with per-chunk sends."],"exampleFix":"// before\nfunc respond(w *wshutil.WshRpc, handler *wshutil.RpcResponseHandler, data any) error {\n    return handler.SendResponse(data, true)\n}\n// after\nfunc respond(w *wshutil.WshRpc, handler *wshutil.RpcResponseHandler, data any) error {\n    if handler.ctx.Err() != nil { return handler.ctx.Err() }\n    return handler.SendResponse(data, true)\n}","handlingStrategy":"try-catch","validationCode":"if handler.ctx.Err() != nil {\n    return handler.ctx.Err() // skip sending; requester already gone\n}","typeGuard":null,"tryCatchPattern":"if err := handler.SendResponse(data, true); err != nil && strings.Contains(err.Error(), \"timeout sending response\") {\n    // requester timed out or disconnected; log at debug, do not retry\n    return nil\n}","preventionTips":["Keep handler work fast or stream incrementally so responses fit in the window.","Raise client-side TimeoutMs for slow operations.","Check handler ctx before beginning expensive response preparation.","Treat late-response failures as benign races rather than errors to retry."],"tags":["rpc","timeout","response-delivery"],"backgroundTag":"rpc-timeout-waiting-for-response","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}