geektutu/7days-golang · error · ErrShutdown
connection is shut down
Error message
connection is shut down
What it means
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.
Source
Thrown at gee-rpc/day5-http-debug/client.go:55
// Client represents an RPC Client.
// There may be multiple outstanding Calls associated
// with a single Client, and a Client may be used by
// multiple goroutines simultaneously.
type Client struct {
cc codec.Codec
opt *Option
sending sync.Mutex // protect following
header codec.Header
mu sync.Mutex // protect following
seq uint64
pending map[uint64]*Call
closing bool // user has called Close
shutdown bool // server has told us to stop
}
var _ io.Closer = (*Client)(nil)
var ErrShutdown = errors.New("connection is shut down")
// Close the connection
func (client *Client) Close() error {
client.mu.Lock()
defer client.mu.Unlock()
if client.closing {
return ErrShutdown
}
client.closing = true
return client.cc.Close()
}
// IsAvailable return true if the client does work
func (client *Client) IsAvailable() bool {
client.mu.Lock()
defer client.mu.Unlock()
return !client.shutdown && !client.closing
}View on GitHub (pinned to cf36443821)
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
Example fix
// before
client.Close()
err := client.Call(ctx, "Foo.Sum", args, reply) // ErrShutdown
// after
if err := client.Call(ctx, "Foo.Sum", args, reply); errors.Is(err, geerpc.ErrShutdown) {
client = dialNewClient() // redial
err = client.Call(ctx, "Foo.Sum", args, reply)
} Defensive patterns
Strategy: try-catch
Validate before calling
// check client usability before calling
func (c *ClientPool) Get() (*geerpc.Client, error) {
c.mu.Lock()
defer c.mu.Unlock()
if c.client == nil || c.closed {
return c.redial()
}
return c.client, nil
} Try / catch
err := client.Call(ctx, "Foo.Sum", args, reply)
if err != nil && strings.Contains(err.Error(), "connection is shut down") {
client = redial(addr) // recreate client and retry once
err = client.Call(ctx, "Foo.Sum", args, reply)
} Prevention
- 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)
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- connection is shut down
- connection is shut down
- rpc client: call failed:
- number of options is more than 1
- rpc server: service/method request ill-formed:
AI-assisted analysis of geektutu/7days-golang@cf36443821 (2026-09-03).
Data as JSON: /api/errors/aa26cd483e08a7eb.
Report an issue: GitHub.