geektutu/7days-golang · error · ErrShutdown
connection is shut down
Error message
connection is shut down
What it means
ErrShutdown indicates the Client connection is no longer usable because it has been closed by the user (closing flag) or told to shut down by the server (shutdown flag). Close() returns it when called twice, and registerCall returns it for new calls on a closed client. It guards against using a dead connection.
Source
Thrown at gee-rpc/day6-load-balance/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
- Track client lifecycle: only call Close() once (e.g. sync.Once) and never issue calls after it
- Create a new client via Dial/DialHTTP/DialTimeout when ErrShutdown is returned
- Wrap client access in a connection pool that discards and reconnects on ErrShutdown
- Check server logs/health: a shutdown flag means the server told the client to stop
Example fix
// before
client.Close()
...
client.Close() // second call returns ErrShutdown
// after
var closeOnce sync.Once
closeOnce.Do(func() { client.Close() })
if err := client.Call(ctx, "Foo.Bar", args, reply); errors.Is(err, ErrShutdown) {
client, _ = DialTimeout("tcp", addr, 3*time.Second)
} Defensive patterns
Strategy: try-catch
Try / catch
if err := client.Call(ctx, "Foo.Bar", args, reply); err != nil {
if errors.Is(err, ErrShutdown) {
client, err = DialTimeout("tcp", addr, 3*time.Second) // reconnect
}
} Prevention
- Close clients with sync.Once to avoid double-Close returning ErrShutdown
- Treat Client as single-use-after-close; rebuild instead of reusing
- Wrap clients in a pool that replaces instances on ErrShutdown
- Monitor server restarts that flip the shutdown flag on connected clients
When it happens
Trigger: Calling Close() on a Client that is already closed; calling Call/Go after Close() or after the server sent a shutdown; calling on a Client whose connection failed during receive().
Common situations: Double-defer of client.Close(); reusing a cached client after the server restarted; long-lived clients hit by server-side idle timeouts then used again.
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/41681a32faa43357.
Report an issue: GitHub.