{"record":{"id":"aa26cd483e08a7eb","repo":"geektutu/7days-golang","slug":"connection-is-shut-down-aa26cd","errorCode":null,"errorMessage":"connection is shut down","messagePattern":"connection is shut down","errorType":"exception","errorClass":"ErrShutdown","httpStatus":null,"severity":"error","filePath":"gee-rpc/day5-http-debug/client.go","lineNumber":55,"sourceCode":"// Client represents an RPC Client.\n// There may be multiple outstanding Calls associated\n// with a single Client, and a Client may be used by\n// multiple goroutines simultaneously.\ntype Client struct {\n\tcc       codec.Codec\n\topt      *Option\n\tsending  sync.Mutex // protect following\n\theader   codec.Header\n\tmu       sync.Mutex // protect following\n\tseq      uint64\n\tpending  map[uint64]*Call\n\tclosing  bool // user has called Close\n\tshutdown bool // server has told us to stop\n}\n\nvar _ io.Closer = (*Client)(nil)\n\nvar ErrShutdown = errors.New(\"connection is shut down\")\n\n// Close the connection\nfunc (client *Client) Close() error {\n\tclient.mu.Lock()\n\tdefer client.mu.Unlock()\n\tif client.closing {\n\t\treturn ErrShutdown\n\t}\n\tclient.closing = true\n\treturn client.cc.Close()\n}\n\n// IsAvailable return true if the client does work\nfunc (client *Client) IsAvailable() bool {\n\tclient.mu.Lock()\n\tdefer client.mu.Unlock()\n\treturn !client.shutdown && !client.closing\n}","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/geektutu/7days-golang/blob/cf3644382101dc13e7fd92e8f5c66cabc51bcd3b/gee-rpc/day5-http-debug/client.go#L37-L73","documentation":"ErrShutdown is the sentinel error returned when an RPC call is attempted on a Client whose connection is no longer usable: either the user called Close (closing=true) or the server told the client to stop (shutdown=true). registerCall refuses to add new pending calls and returns this error, so in-flight calls are also terminated with it.","triggerScenarios":"Calling Call/Go after client.Close(); server sent a shutdown signal or the connection was torn down, terminating all pending calls; reusing a client that another goroutine already closed.","commonSituations":"Long-lived client cached in a struct but closed during application shutdown while other goroutines still use it; server restart dropping the connection; load-balancer idle timeout killing the connection; race between shutdown and request handling.","solutions":["Detect errors.Is(err, geerpc.ErrShutdown) (or string match) and redial a fresh client before retrying the call","Stop issuing calls after Close and coordinate shutdown with context cancellation/WaitGroup","Add connection health checks or automatic reconnect logic in the client wrapper","Investigate server-side shutdown/disconnects if ErrShutdown appears without an explicit Close"],"exampleFix":"// before\nclient.Close()\nerr := client.Call(ctx, \"Foo.Sum\", args, reply) // ErrShutdown\n\n// after\nif err := client.Call(ctx, \"Foo.Sum\", args, reply); errors.Is(err, geerpc.ErrShutdown) {\n\tclient = dialNewClient() // redial\n\terr = client.Call(ctx, \"Foo.Sum\", args, reply)\n}","handlingStrategy":"try-catch","validationCode":"// check client usability before calling\nfunc (c *ClientPool) Get() (*geerpc.Client, error) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tif c.client == nil || c.closed {\n\t\treturn c.redial()\n\t}\n\treturn c.client, nil\n}","typeGuard":null,"tryCatchPattern":"err := client.Call(ctx, \"Foo.Sum\", args, reply)\nif err != nil && strings.Contains(err.Error(), \"connection is shut down\") {\n\tclient = redial(addr) // recreate client and retry once\n\terr = client.Call(ctx, \"Foo.Sum\", args, reply)\n}","preventionTips":["Wrap the client in a pool that redials on ErrShutdown","Coordinate Close with in-flight calls via WaitGroup/context","Add periodic health-check calls to detect dead connections early","Monitor server-side disconnect reasons (restarts, LB idle timeouts)"],"tags":["rpc","connection","shutdown","client-lifecycle","go"],"backgroundTag":"connection-shutdown","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"}