{"record":{"id":"fe1bd9eeb7f09977","repo":"wavetermdev/waveterm","slug":"timeout-sending-cancel","errorCode":null,"errorMessage":"timeout sending cancel","messagePattern":"timeout sending cancel","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/wshutil/wshrpc.go","lineNumber":573,"sourceCode":"\treturn handler.ctx\n}\n\nfunc (handler *RpcRequestHandler) SendCancel(ctx context.Context) error {\n\tdefer func() {\n\t\tpanichandler.PanicHandler(\"SendCancel\", recover())\n\t}()\n\tmsg := &RpcMessage{\n\t\tCancel: true,\n\t\tReqId:  handler.reqId,\n\t}\n\tbarr, _ := json.Marshal(msg) // will never fail\n\tselect {\n\tcase handler.w.OutputCh <- barr:\n\t\thandler.finalize()\n\t\treturn nil\n\tcase <-ctx.Done():\n\t\thandler.finalize()\n\t\treturn fmt.Errorf(\"timeout sending cancel\")\n\t}\n}\n\nfunc (handler *RpcRequestHandler) ResponseDone() bool {\n\tif handler.cachedResp != nil {\n\t\treturn false\n\t}\n\tselect {\n\tcase msg, more := <-handler.respCh:\n\t\tif !more {\n\t\t\treturn true\n\t\t}\n\t\thandler.cachedResp = msg\n\t\treturn false\n\tdefault:\n\t\treturn false\n\t}\n}","sourceCodeStart":555,"sourceCodeEnd":591,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/wshutil/wshrpc.go#L555-L591","documentation":"SendCancelResponse writes a cancel acknowledgment to the RPC output channel; if the surrounding context is done before the write is accepted, the handler is finalized and this error is returned. It indicates the cancel notification itself could not be delivered in time.","triggerScenarios":"Calling SendCancelResponse after the request context was already cancelled/expired; OutputCh backpressure (downstream not consuming) combined with a closing/shut-down context.","commonSituations":"Cancelling a request while the connection is being torn down; caller cancels with an already-expired deadline; slow consumer blocking OutputCh during shutdown.","solutions":["Check ctx.Err() before calling SendCancelResponse; skip the cancel send if the context is already done.","Ensure the connection reader keeps draining OutputCh so the cancel message can be written.","Treat this error as benign during shutdown (the handler was finalized either way) and log at debug level instead of retrying."],"exampleFix":"// before\nerr := handler.SendCancelResponse(ctx)\n// after\nif ctx.Err() == nil {\n    err := handler.SendCancelResponse(ctx)\n}","handlingStrategy":"try-catch","validationCode":"if ctx.Err() != nil {\n    return nil // cancel notification cannot be delivered\n}","typeGuard":null,"tryCatchPattern":"if err := handler.SendCancelResponse(ctx); err != nil && strings.Contains(err.Error(), \"timeout sending cancel\") {\n    // context already done; handler finalized — safe to ignore/log at debug level\n}","preventionTips":["Check ctx.Err() before sending cancel responses.","Keep the output channel drained so writes are not blocked.","Treat cancel-send failures during shutdown as benign.","Avoid cancel calls with already-expired deadlines."],"tags":["rpc","timeout","cancellation"],"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"}