{"record":{"id":"e9c5d4e658a00266","repo":"geektutu/7days-golang","slug":"rpc-client-call-failed-e9c5d4","errorCode":null,"errorMessage":"rpc client: call failed: ","messagePattern":"rpc client: call failed: ","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gee-rpc/day6-load-balance/client.go","lineNumber":191,"sourceCode":"\t}\n\tcall := &Call{\n\t\tServiceMethod: serviceMethod,\n\t\tArgs:          args,\n\t\tReply:         reply,\n\t\tDone:          done,\n\t}\n\tclient.send(call)\n\treturn call\n}\n\n// Call invokes the named function, waits for it to complete,\n// and returns its error status.\nfunc (client *Client) Call(ctx context.Context, serviceMethod string, args, reply interface{}) error {\n\tcall := client.Go(serviceMethod, args, reply, make(chan *Call, 1))\n\tselect {\n\tcase <-ctx.Done():\n\t\tclient.removeCall(call.Seq)\n\t\treturn errors.New(\"rpc client: call failed: \" + ctx.Err().Error())\n\tcase call := <-call.Done:\n\t\treturn call.Error\n\t}\n}\n\nfunc parseOptions(opts ...*Option) (*Option, error) {\n\t// if opts is nil or pass nil as parameter\n\tif len(opts) == 0 || opts[0] == nil {\n\t\treturn DefaultOption, nil\n\t}\n\tif len(opts) != 1 {\n\t\treturn nil, errors.New(\"number of options is more than 1\")\n\t}\n\topt := opts[0]\n\topt.MagicNumber = DefaultOption.MagicNumber\n\tif opt.CodecType == \"\" {\n\t\topt.CodecType = DefaultOption.CodecType\n\t}","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/geektutu/7days-golang/blob/cf3644382101dc13e7fd92e8f5c66cabc51bcd3b/gee-rpc/day6-load-balance/client.go#L173-L209","documentation":"Call() runs the request asynchronously via Go() and blocks on either the context being done or the call's Done channel. If ctx expires or is cancelled first, removeCall drops the pending call and Call returns \"rpc client: call failed: \" + the context error (deadline exceeded or canceled). The actual RPC may still be executing server-side.","triggerScenarios":"Context deadline exceeded before the server response arrives; context cancelled by the caller (e.g. parent request cancelled); server too slow while a short timeout was set on ctx.","commonSituations":"HTTP handler timeout (e.g. 30s) shorter than the RPC's processing time; user navigation cancelling a downstream RPC; misconfigured per-call timeouts under load.","solutions":["Increase the context deadline (context.WithTimeout) to cover expected server latency","Check the server-side handler for slowness (blocking I/O, locks) and optimize","Read the wrapped ctx.Err() in the message: DeadlineExceeded means raise the timeout, Canceled means the caller cancelled","Implement retry with a fresh context, but be aware the original call may have executed (idempotency)"],"exampleFix":"// before\nctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) // too short\n\n// after\nctx, cancel := context.WithTimeout(ctx, 5*time.Second)\nif err := client.Call(ctx, \"Foo.Bar\", args, reply); err != nil {\n    if errors.Is(context.Cause(ctx), context.DeadlineExceeded) { /* raise timeout or retry */ }\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := client.Call(ctx, \"Foo.Bar\", args, reply)\nif err != nil && strings.HasPrefix(err.Error(), \"rpc client: call failed: \") {\n    if ctx.Err() == context.DeadlineExceeded {\n        // retry with a longer deadline, or back off\n    }\n}","preventionTips":["Set call deadlines comfortably above observed p99 server latency","Make RPC handlers idempotent so context-timeout retries are safe","Propagate parent request budgets so a doomed call is cancelled early","Alert on DeadlineExceeded rates to catch server slowdowns early"],"tags":["rpc","context","timeout","go"],"backgroundTag":"context-deadline-exceeded","analyzedSha":"cf3644382101dc13e7fd92e8f5c66cabc51bcd3b","analyzedAt":"2026-09-03T18:31:24.087Z","contentChangedAt":"2026-09-03T18:31:24.087Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}